Skip to content

dfns@dyalog.com

Vector 20:1, page 74

programming techniques language design

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

"They all come from good families, but over the years they kind of individually developed some ways about ’em that just ain’t right." – Tom Waits.

The Dfn-ers

The dfn-ers are a group of people who like to discuss programming in D, a functional subset of Dyalog APL [1, 2]. The group is hosted by Conrad Hoesle-Kienzlen’s Alisia list server at dfns@dyalog.com. If you register as a subscriber and send mail to this address, it is reflected to all of the other subscribers. Anyone can join the group, which currently has around 90 members. It helps if you have Dyalog V9 or better, to run the code fragments that people post, but the discussions are often entertaining in their own right. Subscription is free.

Here is a selection from the last 100 headers:

893 Calling into CHMs
895 Spanned cells (idiom challenge!)
909 Spring Cleaning
916 apl and oop
923 dsamples updates
925 The Beyond
928 Please Ignore …
930 77 keystrokes just for fun
931 Cows and Bulls
933 []EA lookalike for APL2 compilation
934 itching eaches in easter version now removed
935 Yet more left to right
938 Array oriented roman numbers
939 Size of dfns
940 Roman beauty
955 nifty d-op
960 Legitimate children
961 OOP in D
967 I am puzzled - static scope rules
973 Communicating through the code
977 undocumented feature?
978 More on Shy dnfs
979 Rounding operator
988 Losing the result

The group seems to have developed a culture of its own, including an informal (and imprecise) ASCII transliteration scheme:

[]<-1 2{a w}"<iota>3        ⍝ ⎕←1 2{⍺ ⍵}¨⍳3

We have experimented with embedded HTML to display APL characters directly in the body of the email text but on the whole, we didn’t like it; I forget why.

Contributions are sporadic. Sometimes a week will pass with nothing and then there will be a deluge.

In the background, people’s offerings are collated and collected into utility workspace: dfns.dws [3] This is a large and growing repository of good ideas contributed by folk from all over the planet. Each function and operator in the workspace has a corresponding description in the notes namespace. Between us we have so far produced around 2,000 lines of code and 10,000 lines of notes.

As an example, a year or so ago, the group became obsessed with writing variations of the seemingly trivial function:

      3 to 7
3 4 5 6 7

As well as the function itself, these musings finally produced the following notes.to:

nvec ← num #.to num                              ⍝ Sequence ⍺ .. ⍵

Returns a vector of numbers ⍺ to ⍵ inclusive. ⍺ is the first value in the sequence, and ⍵ the last. More generally, ⍺ may be a 2-item vector that determines the first two items of the sequence and hence the step size.

Notice that any of first, step and last may be positive, negative and/or fractional. If last is not a whole number of steps from first, the sequence will stop short of last.

Technical notes:

The following simple function suffices for the ascending sequence ⍺, ⍺+1, … ⍵. Notice how (·-⎕IO) makes it independent of index origin:

to←{(⍺-⎕IO)+⍳1+⍵-⍺}

Useful refinements to the function allow descending sequences and optional step sizes other than 1:

to←{⎕IO←0                                    ⍝ Sequence ⍺ .. ⍵
    from step←1 ¯1×-\2↑⍺,⍺+×⍵-⍺              ⍝ step default is +/- 1.
    from+step×⍳1+0⌈⌊(⍵-from)÷step+step=0     ⍝ ⍺ thru ⍵ inclusive.
}

Attempts to re-cast the function as a one-liner, include:

to←{From+0,+\Step⍴⍨0⌈⌊Inc÷Step←--/2⍴⍺,From+×Inc←⍵-From←1⍴⍺}
to←{↑+/⍵{⍵×{⍵-⎕IO}⍳1+0⌈⌊(⍺⍺-⍺)÷⍵+⍵=0}\1 ¯1×-\2↑⍺,⍺+×⍵-⍺}
to←{(⍬⍴⍺)+(|-/⌽2↑⍺,⍺+1){⍺×(×⍵)×(-⎕IO)+⍳1+⌊|⍵÷⍺}⍵-⍬⍴⍺}

VMJ suggests the following extension, which accepts an optional explicit step size as a second item of its right argument. step defers to next if both are supplied.

to←{
    ⎕IO←0 ⋄ ⍺←1 ⋄ z←0.0000000001
    (a n)←2↑⍺,⍺ ⋄ (b s)←2↑⍵,1
    d←|s{⍵≠0:⍵ ⋄ ⍺≠0:⍺ ⋄ 1}n-a
    {⍵×z<|0-⍵}a+d{(⍺××⍵)×⍳1+⌊|⍵÷⍺}b-a
}

      1 3 to 5            ⍝ using: <next>
1 3 5
      1 to 5 2            ⍝ using: <step>
1 3 5

Examples:
      3 to 10                     ⍝ Inclusive ascending sequence.
3 4 5 6 7 8 9 10
      10 to 3                     ⍝ Descending sequence.
10 9 8 7 6 5 4 3
      7 to 7                      ⍝ Single-item sequence.
7
      5 7 to 13                   ⍝ 2-item start determines step.
5 7 9 11 13
      ¯10 ¯15 to ¯25              ⍝ Negative start and step.
¯10 ¯15 ¯20 ¯25
      1.5 1.7 to 2.5              ⍝ Fractional start and step.
1.5 1.7 1.9 2.1 2.3 2.5
      0 3 to 10                   ⍝ Sequence stops short of <last>.
0 3 6 9

See also: sieve

At one point during this episode, I found myself trying to explain to my wife why an international team of rather clever people were collaborating to write a computer program to count from one to ten.

I didn’t get very far.

Another time, we collectively attempted to write a function isdfn which, given the name of a function, tries to determine if it is a D-function as opposed to a traditional trad-fn. This went on for quite some time, during which we lost at least one subscriber because they couldn’t stand one more isdfn, D-fn.

Apart from these trivial examples, many of the functions and operators in the dfns workspace are both substantial and useful. Here is the current top level index:

Structures.
 Arrays.
 Trees
 Graphs (wGraphs)
 Words
 Strings.
 Line_vectors
 Blank_removal
 Data_compression
 Power.operators
 Function_arrays
 Syntax.operators
 Expressions.
Numbers.
 Whole.numbers
 Real.numbers
 Dates
Workspace.administration
 Workspaces
 Namespaces
 Functions.and.operators
Files.
 Native.files
 Component.files
Diversions.
Performance.

A more useful example:

rvec←{excl}#.refs ref        ⍝ Vector of subspace references.

refs poduces a vector of namespace references to ref and each of its subspaces. The optional left argument is a list of subspaces to be excluded from the traversal.

Some technical points:

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 subspace refs for ⍵.
    ⍺←⍬ ⋄ (⍴,⍺)↓⍺{                          ⍝ default exclusion list.
        1∊⍵=⍺:⍺                            ⍝ already been here: quit.
        ⍵.(↑∇∘⍎⍨/⌽(⊂⍺,⍵),↓⎕NL 9)           ⍝ recursively traverse subspaces.
    }⍵                                     ⍝ 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:

1∊⍵=⍺:⍺                           ⍝ already been here: quit.

This code could be simplified to ⍵∊⍺:⍺ with an enhancement of the interpreter to include refs within the domain of primitive set functions and even omitted altogether by set-subtracting the list of visited refs from the list of subspace refs. In the latter case 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 subspaces. 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 similar to that used in the pow operator.

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: tree up Namespaces

What next?

This is the first of a regular series of columns in Vector describing the activities of the dfns email group. To subscibe to the group, send email to dfns@dyalog.com with subject: subscribe

References:

[1] Dynamic Functions in Dyalog APL, Vector 13.2, 88, available from: www.dyalog.com/download/dfns.zip .

[2] D: A functional subset of Dyalog APL, Vector 17.4, 93

[3] Workspace dfns.dws from www.dyalog.com/download/dsamples.zip .

[4] Jantunen, Veli-Matti, Dynamic Functions in Practice, Vector 18.4, 50