rvec ← {exclude←⍬} ##.refs ref ⍝ Vector of sub-space references. Produces a vector of namespace references to [ref] and each of its subspaces. The optional left argument is a list of sub-spaces to be excluded from the trav- ersal. Technical notes: The coding is complicated by having to cater for "cyclic" namespace references. As a space may contain a reference to one if its ancestors, the code must not blindly follow all subspace references, otherwise unbounded recursion will result in a WS FULL. The trick is to avoid re-visiting any space by maintaining a list of those already visited. refs←{ ⍝ Vector of sub-space references for ⍵. ⍺←⍬ ⋄ (⍴,⍺)↓⍺{ ⍝ default exclusion list. ⍺∊⍵:⍺ ⍝ already been here: quit. ⍵.(↑∇∘⍎⍨/⌽(⊂⍺,⍵),↓⎕NL 9) ⍝ recursively traverse any sub-spaces. }⍵ ⍝ for given starting ref. } The outer code defaults the exclusion list to null (no exclusions) and removes the list from the final result: ⍺←⍬ ⋄ (⍴,⍺)↓⍺{ ⍝ default exclusion list. ··· }⍵ ⍝ for given starting ref. The first line of the inner function stops if the space is in the list: ⍺∊⍵:⍺ ⍝ already been here: quit. The test could be omitted by set-subtracting the list of visited refs from the list of subspace refs. However, the code would have to accomodate the execute of each of a null vector of sub-space names: (1↓⍎¨'0',↓⎕NL 9)~⍺ Perhaps a little daunting at first sight, the second line is interpreted thus: ⍵.(····················) ⍝ Within the subject space, ·················↓⎕NL 9· ⍝ take a vector of sub-space names, ··········(⊂⍺,⍵),······· ⍝ prefixed with extended visited-list. ···↑···⍨/⌽·············· ⍝ left-to-right reduction with operand: ····∇··················· ⍝ recursive call between visited-list ·····∘·················· ⍝ and ······⍎················· ⍝ ref from sub-space name. Notably absent from the code is any explicit test to quit when there are no more sub-spaces. In this case, ↓⎕NL 9 is a null vector, so ⌽(⊂⍺,⍵),↓⎕NL 9 is a 1-item vector, which is just passed along as the result of the reduction without an application of its recursive operand function. The technique is abstracted in operator →trav←. An alternative, suggested by Paul Mansour, which returns just GUI refs is: guirefs←{ ⍝ Vector of GUI references for ⍵. ⍬{ ⍝ starting with null result accumulator. ⍵.(↑∇∘⍎⍨/⌽(⊂⍺,⍵),⎕WN'') ⍝ recursively traverse any sub-spaces. }⍵ ⍝ for given starting ref. } or the equivalent: guirefs←{ ⍝ Vector of GUI references for ⍵. ⍺←⍬ ⍝ starting with null result accumulator. ⍵.(↑∇∘⍎⍨/⌽(⊂⍺,⍵),⎕WN'') ⍝ recursively traverse any sub-spaces. } This code needs no check for circularity as ⎕WN acknowledges only the strict GUI parent-child hierarchy. Again, what is perhaps remarkable, is the lack of any explicit test for termination: guirefs can safely navigate an arbitrarily compl- ex GUI-tree even though it has "no moving parts". Phil Last observes that a ref from a space inside the subject space to one out- side it, will include _all_ subspaces of the outer space. For example: 'x'⎕ns'' x.cuckoo←# refs x #.x # #.notes #.scripts To report only those refs _within_ the subject space (x), Phil suggests replac- ing the recursive line: ⍵.(↑∇∘⍎⍨/⌽(⊂⍺,⍵),↓⎕NL 9) ⍝ recursively traverse any sub-spaces. with: ⍵.(↑∇⍨/⌽(⊂⍺,⍵),⍵{(0 0,1↓⍺=⍵.##)/0,⍵}⍎'⍵',,' ',⎕NL 9) ¯¯¯¯¯¯ The crucial part of the above is ⍺=⍵.##, which produces a mask to extract those subspaces whose parent is the current space. This removes outward pointing refs (such as "cuckoo" above) from the result as its parent is not the current space. The elegance of the expression is marred a little by having to avoid errors if there are no true subspaces. Firstly, if ⎕NL 9 were to return an empty matrix, then ⍎,' ',⎕NL 9 would gener- ate a VALUE ERROR. To avoid this, we preface the possibly empty list of refs with known ref '⍵' (which will be removed later). ⍎'⍵',,' ',⎕NL 9 Secondly, because the interpreter does not currently provide a prototypical ref, we must avoid the NONCE ERROR that would occur if (···⍺=⍵.##)/⍵ resulted in a null (if ⍺=⍵.## produced all zeros). This is achieved by prefixing 0 to the vec- tor of refs: 0,⍵ and removing it with a corresponding 0 as the first item of the replicate: ⍵.(↑∇⍨/⌽(⊂⍺,⍵),⍵{(0 0,1↓⍺=⍵.##)/0,⍵}⍎'⍵',,' ',⎕NL 9) ¯ ¯¯ Finally, the extra prefixed '⍵' from the first step is removed by substituting a 0 for the second item in the replication mask: ⍵.(↑∇⍨/⌽(⊂⍺,⍵),⍵{(0 0,1↓⍺=⍵.##)/0,⍵}⍎'⍵',,' ',⎕NL 9) ¯¯¯¯ ¯¯¯¯ On a related note, Nicolas Delcros points out that this little function: {⍵.##}⍣≡ ⍝ root of ref ⍵: ⎕SE or # given an array of refs of any shape or depth, returns a commensurate array of refs to the root of each item. ⍪¨ ↓ 2 3⍴ ∊ refs¨ # ⎕se ⍝ nested array of refs ┌───────────┬──────────────────────┐ │ # │ ⎕SE │ │ #.notes │ ⎕SE.Dyalog │ │ #.scripts │ ⎕SE.Dyalog.Callbacks │ └───────────┴──────────────────────┘ {⍵.##}⍣≡ ⍪¨ ↓ 2 3⍴ ∊ refs¨ # ⎕se ⍝ corresponding root of each item ┌───┬─────┐ │ # │ ⎕SE │ │ # │ ⎕SE │ │ # │ ⎕SE │ └───┴─────┘ Examples: refs ⎕se ⍝ Vector of space refs for ⎕SE. (refs #).(⎕WX←1) ⍝ Set value of ⎕WX in all spaces in ws. ⌈/(refs #).⎕ml ⍝ Max ⎕ML in workspace. ⌈/{+/'.'=⍕⍵}¨refs # ⍝ Max namespace depth. ⊃+⌿↑⍴¨(refs #).⎕nl 2 ⍝ Total number of global variables. ↑⍪/{⍵ find'⍴⍴'}¨refs # ⍝ Search for rank. ∧/0=(refs #).⎕nc⊂'foo' ⍝ Is this a foo-free workspace? (refs #).⎕ex⊂'foo' ⍝ Defoobarise the workspace. (refs #)fnrepl¨⊂'foo' 'bar' ⍝ In all the fns in all the spaces ... ⎕se.(cbtop cbbot) refs ⎕se ⍝ ⎕SE refs, excluding CoolBands. See also: trav tree up Namespaces xrefs pow Back to: contents Back to: Workspaces