Skip to content

More on Execute Inverse

Vector 21:1, page 140

programming techniques

Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 140

In Volume 20.3, the Zark Newsletter discussed Execute Inverse and also included submissions by Nicholas Small and myself as a separate articles. I learnt that '''',((1+⎕AV='''')/⎕AV),'''' may not be executable. Dyalog APL reports a SYNTAX ERROR due to just a single character, the first or nul character.

Rex Swain offered two solutions for APLs respectively without and with nested structures as originally conceived in APL*PLUS NARS by STSC. Oddly, the solution without nesting overlooked the problem of non-executable characters. The second devoted quite a lot of code in substituting the APL*PLUS’s ⎕TCnnn characters (e.g. ⎕TCNUL) within a text string, e.g. '...',⎕TCNUL,'...' .

With the benefit of hindsight, I have improved my original submissions to handle non-executable characters by substituting an indexed ⎕AV[⎕IO+n n n] code string rather than using APL*PLUS system variables.

The most concise coding would produce output with no special-casing; with nested elements as (⊂...),(⊂...)... ; and with the explicit reshape of all sub-arrays. It would have redundant parentheses. Altogether not very readable but with the fastest execution time, typically 25-50% faster.

The results from the functions below are clear, much as one might enter an expression via the desktop. There are no redundant parentheses or spaces. Text null is presented without an explicit reshape as '' while the numeric null is presented as ⍬ (zilde) in the DFn (Dyalog-specific) but as 0⍴0 in the more general function. The unit vector is presented as e.g. ,5 or ,'x'. There is no explicit reshape for scalars or vectors of length greater than 1. Strand notation is used. Items of text vectors are not enclosed in parentheses but separated by space, except for strings with non-executable characters. No attempt is made to compress, say, an item of repeated elements.

The first submission is a Dyalog APL dynamic function (DFn). There are no local variables at all apart from a single static (nul). DFns encourage functional programming.

Notice how the operands of a dynamic operator (DOp) may be used to pass effectively one or two extra array arguments; operand ⍺⍺ of Dop ad is the current sub-array whose ⍺’th element is being processed.

ei←{⎕PP←15 ⋄ nul←⍬⍴⎕AV                          ⍝ String that reproduces ⍵ when executed
    sh←{⍺≡⍬:'⍬' ⋄ ⍺≡'':'''''' ⋄ (,1)≡⍴⍺:',',⍵      ⍝ reSHape - special cases
        1∊(0 1=⍴⍴⍺)∧1,1<⍬⍴⍴⍺:⍵ ⋄ (⍕⍴⍺),'⍴',⍵      ⍝ condit`l explicit reshape
    }
    ad←{1=×/⍴⍺⍺:'⊂',⍵ ⋄ np ⍺⊃⍺⍺:(⍺=⎕IO)↓' ',⍵ ⋄ 1⌽')(',⍵} ⍝ ADorn nested(dop)
    np←{(~nul∊⍵)∧(0=≡⍵)∨(⍵≡⍬)∨(' '≡⍬⍴0⍴⍵)∧(1=⍴⍴⍵)∧1≠⍬⍴⍴⍵}     ⍝ No Parens
    qu←{(1+⍵='''')/⍵}                                ⍝ double-up QUotes
    pt←{⎕ML←0 ⋄ ({⍵≠¯1↓1,⍵}⍵=nul)⊂⍵}                 ⍝ ParTition on nul chars
    tx←{nul=⍬⍴⍵:6⌽'⎕IO],'''',⎕AV[',(1<⍴,⍵)/(⍕⍴⍵),'⍴' ⋄ ⍵}  ⍝ process TeXt
    tr←sh∘{                                          ⍝ TRaverse array
        326=⎕DR ⍵:↑,/(⍳⍴⍵)(⍵ ad)¨⍵ tr¨⍵             ⍝ nested or mixed simple
        0=⍬⍴0⍴⍵:⍕⍵                                    ⍝ numeric
        ~nul∊⍵:1⌽qu'''',,⍵                            ⍝ normal text
        ↑,/tx¨pt 1⌽qu'''',,⍵                          ⍝ text with nul chars
    }∘{(1⌈⍴,⍵)⍴⍵}                                     ⍝ expose prototype

    ⍵ tr ⍵                                            ⍝ do it
}

The second, slightly slower, solution is a more general function that should work, perhaps with minor adjustments, in most APLs that support control structures which may, of course, be re-coded easily to use branches and labels. Non-executable characters are identified as the origin-0 indices into ⎕AV in line [1] to suit the version of APL.

This example is fine for Dyalog APL.

    ∇ r←ei_n b;i;l;ne;x;y                    ⍝ String that reproduces b when executed

[1]   ne←⎕AV[⎕IO+0]                         ⍝ non-executable chars - inc as appropriate
[2]   y←(1⌈⍴,b)⍴b                                                   ⍝ expose null
[3]   :If 326=⎕DR b ⋄ r←ei_n¨y                                 ⍝ nested or mixed simple
[4]       :For i :In ⍳⍴r
[5]           :If 1=×/⍴y ⋄ (i⊃r)←'⊂',i⊃r                        ⍝ single strand item
[6]           :ElseIf (~1∊x∊ne)∧(0=≡x)∨(' '≡⍬⍴0⍴x)∧(1=⍴⍴x)∧1≠⍬⍴⍴x←i⊃y
[7]               (i⊃r)←(i=⎕IO)↓' ',i⊃r                ⍝ no parens around strand item
[8]           :Else ⋄ (i⊃r)←1⌽')(',i⊃r                                ⍝ parens required
[9]           :EndIf
[10]      :EndFor
[11]      r←↑,/r
[12]  :ElseIf 0=⍬⍴0⍴b ⋄ r←⍕y                                                ⍝ numeric
[13]  :Else                                                                 ⍝ text...
[14]      r←1⌽(1+r=⍬⍴r)/r←'''',,y                                         ⍝ do quotes
[15]      :If 1∊ne∊r                                   ⍝ modify runs of non-exec chars
[16]          r←(l≠¯1↓1,l←r∊ne)⊂r                         ⍝ partition on non-exec chars
[17]          :For i :In ⍳⍴r
[18]              :If 1∊ne=⍬⍴i⊃r                                      ⍝ as ',⎕AV[n n n],'
[19]                  (i⊃r)←3⌽'],'''',⎕AV[⎕IO+',⍕|⎕IO-⎕AV⍳i⊃r
[20]              :EndIf
[21]          :EndFor
[22]          r←↑,/r
[23]      :EndIf
[24]  :EndIf
[25]  :If b≡'' ⋄ r←''''''                                               ⍝ special-case ''
[26]  :ElseIf (,1)≡⍴b ⋄ r←',',r                                    ⍝ single item vector
[27]  :Else ⋄ r←((~1∊(0 1=⍴⍴b)∧1,1<⍬⍴⍴b)/(⍕⍴b),'⍴'),r                       ⍝ reshape
[28]  :EndIf
    ∇

[Note: A Dyalog 8.2 workspace (22KB) containing these functions may be downloaded from www.aplforum.com]