Notes about Execute Inverse Functions
Vector 20:3, page 33
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 33
I submit several variations as my solutions to the Zark challenge in Vector Vol 20 No 2 to write an Execute Inverse function. I have frequently used functions with this specification in the past (often known as UnExecute) for various purposes, for example, to transfer variables between different APLs. More recently, I have developed a version for my Variable Editor to present an editable expression to the user, though this exercise has proven useful in bringing to light one or two deficiencies.
My first solution, the fastest, is a Dyalog dynamic function name EI1:
EI1←{⎕PP←15 ⍝ String that reproduces ⍵ when executed - yukky display
sh←{⍺≢⍬:(⍕⍺),'⍴',⍵ ⋄ ⍵} ⍝ reshape
ad←{⍺:1⌽'),(⊂',⍵ ⋄ ⍵} ⍝ adorn with ,(⊂...)
ei←{⍺←0 ⍝ ⍺=1 when recursing
s←⍴⍵ ⍝ shape
⍬≡,⍵:⍺ ad s sh'0' ⍝ empty numeric
''≡,⍵:⍺ ad s sh'''''' ⍝ empty text
326=⎕DR ⍵:⍺ ad s sh{ ⍝ nested or heterogeneous...
0=⍴⍵:1↓⊃,/1 ei¨1⍴⍵ ⍝ empty - assemble prototype
1↓⊃,/1 ei¨⍵ ⍝ recurse on nested elements
},⍵
0≡⊃0⍴⍵:⍺ ad s sh⍕,⍵ ⍝ numeric
⍺ ad s sh 1⌽'''''',(1+''''=,⍵)/,⍵ ⍝ quotes round text/double quote
}
ei ⍵
}
The code could be 3 lines shorter by including the empty cases with the non-empty cases and modifying ⍵ to (1⌈⍴,⍵)⍴⍵ (see next function). However, my time tests showed a significant time improvement with specific handling of the empty cases.
You will note that I have set ⎕PP to 15 rather than 16. This is done to reduce the output of real numbers. The number 6.6, for example, would otherwise be displayed as 6.5999999999999999 rather than 6.6.
I use ⎕DR, available in both Dyalog and APL2000, to determine data representation (type) rather than pure, but slower, APL idioms. Note in particular that simple heterogeneous arrays are handled through recursive calls. The ⎕DR of both nested and heterogeneous arrays is 326. By testing for these structures first, the identification of simple numeric or text arrays is simplified.
I do not cater for ⎕OR elements and References in arrays. I felt that handling these items is beyond the scope of this exercise, and indeed I wonder if there is a way to reliably reproduce a Reference! Come to that, the ⎕OR of a namespace would not be too easy to reproduce.
My second submission is a normal user-defined function, though I do use Dyalog control structures which are implemented in other versions of APL. It is of course a simple exercise to convert the control structures to good old-fashioned branch and label equivalents. The function is named EI2:
∇ r←{a}EI2 w;⎕PP;x
[1] ⍝ String that reproduces ⍵ when executed - conventional fn/yukky display
[2] ⍝ a ←→ 1:indicates recursion
[3] ⎕PP←15
[4] a←1≡⍎⊃(0=⎕NC'a')⌽'a0' ⍝ a=1 when recursing
[5] :If 326=⎕DR w ⋄ r←1↓⊃,/1 EI2¨(1⌈⍴,w)⍴w ⍝ nested or heterogeneous
[6] :ElseIf 0≡⊃0⍴w ⋄ r←⍕(1⌈⍴,w)⍴,w ⍝ numeric
[7] :Else ⋄ r←1⌽'''''',(1+''''=x)/x←(1⌈⍴,w)⍴w ⍝ quotes around/double quote
[8] :EndIf
[9] :If ⍬≢⍴w ⋄ r←(⍕⍴w),'⍴',r ⋄ :EndIf ⍝ reshape
[10] :If a ⋄ r←1⌽'),(⊂',r ⋄ :EndIf ⍝ addorn with ,(⊂...)
∇
This function is about 50% slower than the Dfn, though there was no consistent timing difference with or without special-casing of empty items so I have opted for the shorter form.
What I do not like about the above functions, which produce identical results, is that though they conform to the requirement that ⍵≡⍎EI⍵ (except that they will fail if a variable includes ⎕OR’ed or Reference elements), the displayed result is awful, lots of parentheses and reshapes, and the long-winded form for arrays of text vectors.
My third submission, another Dyalog dynamic function, returns a result that is far more readable, with no redundancy, and close to the preferred way in which most people would enter the expressions at the desktop, or in applications where the executable form may be presented for user editing, e.g. as in my Variable Editor.
As this is my favourite, I call it EI. It is slower than the first Dfn, but is comparable to the second conventional function in my timing tests.
EI←{⎕ML←0 ⋄ ⎕PP←15 ⍝ String that reproduces ⍵ when executed - friendly display version
sh←{⍝ reshape ⍵ according to shape ⍺
⍺≡,1:',',⍵ ⍝ ravel rank-1 singltn (shorter)
1∊(1=⍴⍴⍺)∧(0 1=⊃⍴⍺)∧1,0<⊃⍺:⍵ ⍝ vector, null or length>1
(⍕⍺),'⍴',⍵ ⍝ include explicit reshape
}
ad←{⍝ adornments on ⍵ given by ⍺
⍺=1:'(',⍵,')' ⍝ in parentheses
⍺=2:' ',⍵ ⍝ precede with space
⍵ ⍝ no change
}
ei←{⍝ main dfn: recursive call if ⍺
⍺←0 ⍝ default not a recursive call
s←⍴⍵ ⍝ shape
⍬≡,⍵:(⍺×1<⍴s)ad⊃(1<⍴s)⌽'⍬'((⍕s),'⍴0') ⍝ is ⍬
''≡,⍵:(⍺×1<⍴s)ad((1<⍴s)/(⍕s),'⍴'),'''''' ⍝ is ''
(s≡⍬)∧1=≡⍵:2 ad'''***⎕OR***''' ⍝ ⎕OR: cannot handle
(s≡⍬)∧9=⊃⎕NC'⍵':2 ad'''***Ref:',(⍕⍵),'***''' ⍝ Reference: cannot handle
326=⎕DR ⍵:⍺ ad s sh s{ ⍝ nested or heterogeneous...
0=⍴⍵:'⊂',i↓(-i←'('=⊃x)↓x←1 ei⊃⍵ ⍝ empty
x←(' '=⊃x)↓x←⊃,/1 ei¨⍵ ⍝ recurse on nested elements
(0=⍴⍺)∨(1=⍴⍺)∧1=⊃⍺:'⊂',i↓(-i←'('=⊃x)↓x ⍝ strip parens and enclose
x ⍝ unmodified
},⍵
0≡⊃∊⍵:(⍺×1+0=⍴s)ad s sh⍕,⍵ ⍝ numeric
(⍺×1+1≥⍴s)ad s sh 1⌽'''''',(1+''''=,⍵)/,⍵ ⍝ quotes round text/double quote
}
ei ⍵
}
And that is it.
David Crossley
20th November 2003
St Didier