Hacker’s Corner: Clipboarding APL Arrays
Vector 13:3, page 90
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 90
Motivation¶
I spent a good few weeks this autumn making sure that the versions of Rain and NewLeaf in Dyalog APL and APL+Win were completely compatible. Pasting blocks of APL code from one to the other (in either direction) is no longer a problem, in fact I have taken to editing code in APL+Win even if the final target is Dyalog — the benefits of code-illumination (so easy to spot accidental globals when they show in yellow) outweigh the minor hassle of a copy+paste.
However, there remained one significant barrier. How do you move an APL array across the great divide? I particularly needed to move NewLeaf page layout definitions, which are deeply nested matrices such as:
⎕se.Disp rpt.toc
┌──┬───────────────┬─────────┬─────────┬─────────────────┐
│pg│Table of Con...│200 28 │597 845 │┌──────┬────────┐│
│ │ │ │ ││Orient│Portrait││
│ │ │ │ │└──────┴────────┘│
├──┼───────────────┼─────────┼─────────┼─────────────────┤
│fr│Body │72 72 │¯144 ¯144│ │
├──┼───────────────┼─────────┼─────────┼─────────────────┤
│tx│&r │72 32 │¯144 0 │ │
├──┼───────────────┼─────────┼─────────┼─────────────────┤
│tx│Table of Con...│198.5 794│¯397 0 │┌────┬───┐ │
│ │ │ │ ││Font│tib│ │
│ │ │ │ │├────┼───┤ │
│ │ │ │ ││Size│24 │ │
│ │ │ │ │└────┴───┘ │
└──┴───────────────┴─────────┴─────────┴─────────────────┘
… so that I could design in either APL and test the layout on the other one. Here is one (very non-technical) solution to the problem.
Solution¶
For every array, there exists an APL expression which executes to produce it! This expression is a simple character vector, so it should clipboard with no problem at all. When pasted into the ‘other’ interpreter, all you need to do is to press <Enter> to regenerate the original array — simple really.
xx←'Hello, ''world''' (2 3⍴⍳6)
lfmt xx
('Hello, ''world''')(2 3⍴1 2 3 4 5 6)
⍎lfmt xx
Hello, 'world' 1 2 3
4 5 6
xx≡⍎lfmt xx
1
Here is what I hacked out, with very little thought as to the design, more or less on a case by case basis:
∇ flat←lfmt any;⎕ML
[1] ⍝ Take any array and flatten it for clipboarding
[2] ⎕ML←3 ⋄ flat←''
[3] ⍝ Does it have serious shape at this level?
[4] :If 1<⍴⍴any
[5] flat←flat,(⍕⍴any),'⍴'
[6] any←,any
[7] :If 0=⍴any
[8] flat←flat,'⍬'
[9] :End
[10] :End
[11]
[12] ⍝ Do we need to go deeper?
[13] :If 1<|≡any
[14] :Select ↑⍴,any
[15] :Case 0
[16] flat←flat,'⍬'
[17] :Case 1
[18] flat←flat,↑,/'(,⊂',(lfmt↑any),')'
[19] :Else
[20] flat←flat,↑,/'(',¨(lfmt¨any),¨')'
[21] :EndSelect
[22] :Else
[23] :If ' '=1↑0⍴any
[24] any←(1+any='''')/any
[25] any←'''',any,''''
[26] :End
[27] flat←flat,⍕,any
[28] :End
[29]
[30] ⍝ Unfortunately () is not valid APL!!
[31] :If 0=⍴flat
[32] flat←'⍬'
[33] :End
∇
This still fails to produce exact matches on deeply nested empty things, but I really don’t care enough to bother — perhaps a keen reader would like to give it some rigorous testing and have fun pointing out all the horrid errors!
As a final aside, I recently came across a paper (dated 1976) which proposed control structures very like those used above. I can’t help feeling that if we had had these 20 years ago, APL might still be a mainstream language.