So far, we have been reading and writing dfns.
3 {⍺+⍵} 5 ⍝ Left argument ⍺, right argument ⍵
{⍵>0:⍵,∇ ⍵-1 ⋄ ⍵}5 ⍝ Guard is : (colon). The function itself is ∇ (del)
Fn ← {⍺⍵} ⍝ We can give functions names
It is also possible to name functions without explicitly referring to their arguments. This is called tacit or point-free programming.
Plus ← +
IndicesTo ← ⍳
_Reduce ← /
Sum ← Plus _Reduce
Sum IndicesTo 10
There is a syntax for composing functions called trains. We won’t look at them in detail now, but they allow some rather neat and memorable ways to write short functions.
Mean ← +⌿ ÷ ≢ ⍝ The sum divided by the count
Mean 3 1 4 1
3 (+,-) 5 ⍝ Plus and minus
','(≠⊆⊢)'some,text' ⍝ Split on commas
i
Note:
Small unnamed dfns and tacit functions expand your vocabulary. One of my favourites is the "split by delimiter" train (≠⊆⊢)
. It looks like a beat-up face kaomoji. A similar phrase which can take multiple delimiters can be found on aplcart.info.
Dyalog is a modern APL implementation. Since the first APL implementations there has been a way of defining functions with a header line and named arguments and results. Since the introduction of dfns, functions of the original style are called traditional functions or tradfns.
Mean ← +⌿÷≢ ⍝ A 3-train (fork) for the arithmetic mean
Mean ← {(+⌿⍵)÷≢⍵} ⍝ A dfn for the arithmetic mean
∇ m ← Mean a ⍝ A tradfn for the arithmetic mean
m ← (+⌿a) ÷ ≢a
∇
i
Note:
Copy and paste everything between (and including) the two ∇
del symbols into the session, and press Enter
, to define a tradfn in your workspace.
Using Shift+Enter
with the cursor on a name will bring up an editor window for that named thing.
A tradfn header reflects the calling syntax of the function.
∇ {result}←{optional}TradFn argument;local1;local2
[1] :If 0=⎕NC'optional'
[2] optional←'no left argument'
[3] :EndIf
[4] local1←'⍺: ',optional
[5] local2←'⍵: ',argument
[6] global←⍪'TradFn last called with'local1 local2
[7] result←⍪local1 local2
∇
TradFn
with one and two arguments. How can the result be made to display to the session?global
after calling TradFn
with different arguments.Ctrl+Enter
. Inpect ⎕NC'optional'
when TradFn
is called with one argument and when it is called with two arguments.Here is the smallest tradfn:
∇ T
∇
T
is a function which takes no arguments, returns no results and has no effects.
Results in {}
curly braces do not print to the session by default, but can be passed to arguments. To ease debugging and write functions with predictable behaviour, it is generally best not to use shy results.
Optional left arguments are a little awkward in tradfns. The dfn equivalent is a little nicer looking: {⍺←'default' ⋄ ⍺,⍵}
.
While usage of different function styles varies throughout many applications, I suggest these non-prescriptive guidelines, from Adám’s APL Style Guide, for production code. When maintaining others’ code, it is best to try to continue in the already established style.
For medium sized functions and utilities. Nested dfns are fine, but never use multi-line dfns inline.
MultiDfn←{ ⍝ A Dfn with nested in-line multi-line dfns
(3{ ⍝ These are confusing to read and trace through
⍺+2×⍵
}⍺){
x←⍺-4+⍵
x-2×⍺
}3+⍵
}
Instead, give them names and then apply them. Named dfns should be multi-line so that they can be traced through, unless truly trivial.
MultiDfn2←{ ⍝ The previous function rewritten more clearly
y←3+2×⍺
x←y-1+⍵
x-2×y
}
Do not use a dfn instead of naming a variable. For example, instead of
r←{⍵/⍨10≤⍵}a,b
write
candidates←a,b
r←candidates/⍨10≤candidates
Best as short, pure functions, performing some specific task such as data transformation. Trains and functions derived from functions and operators (e.g. +/
) can be used inline if they are not too complex.
Best used as program control and for dealing with system interactions. The use of control structures can make procedural tasks easier to debug. For example, if an error occurs during a loop or iteration.
¯5{i←⍺+⍳⍵ ⋄ i÷i-2}10 ⍝ A single line function cannot be traced through
i
Note:
💡 Use Ctrl+Enter
to step through a multiline function. You can then use Shift+Enter
to edit the function during execution and Esc
to save your changes to the function and continue execution.
∇ r←a MultiLineError o;i
[1] :For i :In a+⍳o
[2] r←i+3
[3] r÷r-2
[4] :EndFor
∇
{⌈/≢¨⍵}
{1+⍺-⍵}
{∨/⍺∊⍵}
{(⌽⍵)≡⍵}
(⌈/-⌊/)
(+⌿÷1⌈≢)
(⊢-|)
(1∧⊢,÷)