Workspace Management
Vector 4:2, page 99
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 99
The idiom which I call ‘del-all-dupes’ (see Technical Letters on page 96) was invented (or, more likely, re-invented) for a particular application. But then, as often seems to happen, I found another couple of uses for it within the space of a few days, both of which arose when playing with some workspace management functions. (Although many commercial APL packages have a good set of WS management functions bundled with them, there are times when you may need to generate your own, as I was doing at the time.)
The first example arose when enlarging a simple function to list the WS functions, but excluding the listing function itself, and one or two others. Instead of the more traditional ways of removing selected names from a table, it seemed that an interesting variant would be simply to add the unwanted names and then use the above idiom to get rid of them. This is done in LISTFNS, which in this instance is set up to give just a screen display. (CHAIN1 is a simple function for vertical catenation and ALPHSORT will be self-explanatory.)
LISTFNS;M;W;Y
-------------
[1] ⍝ Display (almost) all functions in WS with ⎕CR
[2] W←¯1↑⍴M←⎕NL 3
[3] ⍝ First remove unwanted names by adding them
[4] ⍝ and using the DEL-ALL-DUPES idiom ...
[5] M←M CHAIN1 'LISTFNS' CHAIN1 'LISTVARS' CHAIN1 'ALPHSORT'
[6] M←(~+/<\Y-<\Y←M∧.=⍉M)⌿M
[7] M←ALPHSORT M
[8] ⍝ Now loop through list ...
[9] Lp:' '
[10] ' '
[11] →(0=1↑⍴M)/End
[12] ⎕CR,(1,W)↑M
[13] →Lp ⋄ M← 1 0 ↓M
[14] End:'** DONE **'
∇
The other case was the somewhat more difficult task of identifying only the global names in a function. Along with other sources, Chapter 10 of Adrian Smith’s book “APL - A Design Handbook for Commercial Systems” provides a sound basis for generating WS management functions, but I have always thought it was a slight pity that his function ∆STRIP (p.128), which aims to cut out most unwanted names etc., doesn’t get rid of labels as well. Of course, as he comments, if you are surveying a complete WS the ‘and dot equals’ against ⎕NL 2 and 3 will do most of the work - but this doesn’t cover the case where the same label mame is used both locally and globally (and I have been caught by that before!). Also, I like to have a stand-alone ‘stripper’ to look at individual functions without doing the whole workspace each time. As well as labels, I sometimes like to knock out QUAD names (as here), although there can be a case for keeping the function modifiers (⎕FX, etc.).
My extended version of ∆STRIP is listed below. Firstly, the check in line 9 avoids an INDEX ERROR if the name is mis-typed. Then QUAD and the colon are included in ALPH, and removal of QUAD names in line 38 is straightforward since the quad symbol will always appear first. Labels are a bit more tricky, though. We need the colon to identify them (and it can be anywhere on the line except first), and we have to get rid of the name, both with and without the colon attached. The way I have chosen is shown in lines 26 onwards: genuine duplicates must be reduced to 1 copy first (otherwise they get lost later!) and, as usual, some extra blanks must be included here to avoid names coalescing. Then by replacing the colons with blanks, and tidying up, the ‘del-all-dupes’ idiom in line 32 (which, to re-emphasise, removes all entries of a duplicated name) can finish the job (well, almost - see below). It is also necessary to move the deletion of numerics and header names lower down, in case they are associated with labels; and I have taken the liberty of putting in a couple of extra local variables, which I find helps to make the switching between vectors and matrices a bit easier to follow.
Adrian Smith notes on p.128 of his book that this function contains a “…remarkable selection of the more obscure idioms…”. Perhaps I have added to this obscurity, but I think it does demonstrate an important point in APL, which, although well-known to many, is always worth re-emphasising: idioms provide another level of modularity in APL (below functions).
A good idiom is origin-independent (unless specifically stated otherwise), handles null conditions correctly, and produces a well-formed output from a well-formed input. Thus, using idioms, one can often proceed through large chunks of a quite complex task without having to keep track of branches, loops, labels, and a large collection of variable names, (obscure or otherwise). One also has a slightly greater sense of security that the end result should not fall over in unusual conditions - although this must not be taken for granted.
Which brings me back to the ‘almost’ remark above. There are several things the extended function still won’t do. Firstly, there is the rather sneaky case of a label set, and not used, but at least it is picked out. More important, though, are any implicit global references which appear (wholly or partly) within quotes. I have already referred in passing to ⎕FX etc., and simple cases of this kind are not too difficult to deal with automatically. It is slightly harder when techniques such as boolean mapping onto tables of function names have been used, but the one I really don’t like much is a direct execute of a global name which is assembled by catenating characters and local variables. I don’t know any ‘clean’ way of dealing with things like that, except to highlight them for visual examination, and I would be interested to hear how others have tackled these problem areas.
R←∆STRIPX FN;HEAD;ALPH;V;M
--------------------------
[1] ⍝ Chop FN to non-localised names
[2] ⍝
[3] ⍝ Adrian Smith's fn (P128) extended to also remove
[4] ⍝ ⎕names and labels. ** P.B.B.
[5] ⍝ (** indicates additions **)
[6] ⍝
[7] ⍝ ** check that FN is in WS **
[8] →(1≠+/M∧.=(1↓⍴M←⎕NL 3)↑FN)/Fail
[9] ⍝ ** add ':⎕' (and ⍙) to ALPH **
[10] ALPH←':⎕','ABCDEFGHIJKLMNOPQRSTUVWXYZ∆⍙'
[11] ALPH←ALPH,'0123456789abcdefghijklmnopqrstuvwxyz'
[12] FN←⎕CR FN
[13] ⍝ Header checklist for later
[14] HEAD←' ;←' ∆TAB FN[1;]
[15] M← 1 0 ↓FN
[16] ⍝ Cut out comment lines, ravel ...
[17] V←,((~M∨.='⍝')⌿M),' '
[18] ⍝ Take out if inside quotes ...
[19] V←(~(V='''')∨≠\V='''')/V
[20] ⍝ Blank for non-alph ... kill mult blanks
[21] V[(~V∊ALPH)/⍳⍴V]←' '
[22] V←(~(' '=V)∧' '=1⌽V)/V
[23] ⍝ Rest of names as table ...
[24] M←' ' ∆TAB V
[25] ⍝ ** Cut out genuine dups(leave 1 copy); ravel **
[26] V←,((1 1 ⍉<\M∧.=⍉M)⌿M),' '
[27] ⍝ ** ':' ↔ blank for labels **
[28] V[(':'=V)/⍳⍴V]←' '
[29] ⍝ ** remove dupl. blanks; reform table **
[30] M←' ' ∆TAB V←(~(' '=V)∧' '=1⌽V)/V
[31] ⍝ ** Cut out all copies of dups (labels) **
[32] M←(~+/<\R-<\R←M∧.=⍉M)⌿M
[33] ⍝ Cut out if all numeric ...
[34] M←(~∧/M∊' 0123456789')⌿M
[35] ⍝ Remove names in header ....
[36] M←(~∨/M∧.=⍉((1↑⍴HEAD),1↓⍴M)↑HEAD)⌿M
[37] ⍝ ** take out if ⎕NAME **
[38] M←(('⎕'≠M)[;1])⌿M
[39] →0,0⍴R←ALPHSORT M
[40] Fail:'INVALID FN NAME'
[41] ⍝ ** Index Error if FN is locked, or 1-line null **
∇
⍝ (ED) ALPH WAS ⎕AV[65+⍳64] ... DOES NOT WORK ON PC!!
**** TEST <∆STRIPX> ON ITSELF ****
∆STRIPX'∆STRIPX'
ALPHSORT
∆TAB
LISTSTRIP;M;W;Y;M1
------------------
[1] ⍝ Display global names in (almost) all WS fns
[2] W←¯1↑⍴M←⎕NL 3
[3] ⍝ Remove unwanted names using DEL-ALL-DUPES
[4] M←M CHAIN1 'LISTFNS' CHAIN1 'LISTVARS' CHAIN1 'ALPHSORT'
[5] M←M CHAIN1 'LISTSTRIP' CHAIN1 '∆STRIPX'
[6] M←(~∧/<\Y-<\Y←M∧.=⍉M)⌿M
[7] ⍝ Remove blank rows for neatness ..
[8] M←ALPHSORT M←(M∨.≠' ')⌿M
[9] ⍝ Now loop through the list ...
[10] Lp:' '
[11] →(0=1↑⍴M)/End
[12] M1←∆STRIPX Y←,(1,W)↑M
[13] ⎕←Y CHAIN1(W⍴'¯')CHAIN1 M1
[14] →Lp ⋄ M← 1 0 ↓M
[15] End:'** DONE **'
∇
As a further thought, I haven’t looked at Roger Harper’s “Workspace Lister” in the BAA Public Domain Software Library, but in any case this appears from the description to be specific to APL*PLUS/PC. Does anyone have a good set of workspace management tools, for a reasonably complete sub-set of APL (ISO-APL?), which they could pass on to the Software Library?
FOOTNOTE: at the last minute, I have found a little time to look at IBM’s VS APL WS management functions in UTILITY 1. However, after copying NAMEGP to another WS, I found that NAMETYPES would no longer pick up labels. Also, NAMEREPLACE has a labelled branch but the label (L2) is not set. Perhaps these (and other?) little quirks are well known to others, but I don’t think I’ll bother with that set of functions anymore!