Content deleted Content added
m Open access bot: doi added to citation with #oabot. |
m Open access bot: url-access updated in citation with #oabot. |
||
(26 intermediate revisions by 18 users not shown) | |||
Line 1:
{{Short description|Alternate way to define a function in APL}}
{{redirect|Dfns|the political entity sometimes known as the Democratic Federation of Northern Syria|
A '''direct function''' ('''dfn''', pronounced "dee fun") is an alternative way to define a function and operator (a [[higher-order function]]) in the programming language [[APL (programming language)|APL]]. A direct operator can also be called a '''dop''' (pronounced "dee op"). They were invented by [[John M. Scholes|John Scholes]] in 1996.<ref name=Scholes1996>{{cite journal|last=Scholes|first=John|title=Direct Functions in Dyalog APL|journal=Vector|volume=13|number=2|date=October 1996|url=http://www.dyalog.com/uploads/documents/Papers/dfns.pdf|access-date=16 September 2019}}</ref> They are a unique combination of [[array programming]], higher-order function, and [[functional programming]], and are a major distinguishing advance of early 21st century APL over prior versions.
A dfn is a sequence of possibly [[Guard (computer science)|guarded expressions]] (or just a guard) between {{code|{|apl}} and {{code|}|apl}}, separated by {{code|⋄|apl}} or new-lines, wherein {{code|⍺|apl}} denotes the left argument and {{code|⍵|apl}} the right, and {{code|∇|apl}} denotes [[Recursion (computer science)|recursion]] (function self-reference). For example, the function {{code|PT|apl}} tests whether each row of {{code|⍵|apl}} is a [[Pythagorean triplet]] (by testing whether the sum of squares equals twice the square of the maximum).
<
PT← {(+/⍵*2)=2×(⌈/⍵)*2}
PT 3 4 5
Line 17 ⟶ 18:
PT x
1 0 1 0 0 1
</syntaxhighlight>
{{Anchor|factorial}}
The [[factorial]] function as a dfn:
<
fact← {0=⍵:1 ⋄ ⍵×∇ ⍵-1}
fact 5
Line 28 ⟶ 29:
fact¨ ⍳10 ⍝ fact applied to each element of 0 to 9
1 1 2 6 24 120 720 5040 40320 362880
</syntaxhighlight>
== Description ==
The rules for dfns are summarized by the following "reference card":<ref name=refcard>{{citation|last=Scholes|first=John|title=Direct Functions Reference Card|date=
{| class="wikitable"
Line 56 ⟶ 57:
A dfn is a sequence of possibly [[Guard (computer science)|guarded expressions]] (or just a guard) between {{code|{|apl}} and {{code|}|apl}}, separated by {{code|⋄|apl}} or new-lines.
<
expression
guard: expression
guard:
</syntaxhighlight>
The expressions and/or guards are evaluated in sequence. A guard must evaluate to a 0 or 1; its associated expression is evaluated if the value is 1. A dfn terminates after the first unguarded expression which does not end in [[Assignment (computer science)|assignment]], or after the first guarded expression whose guard evaluates to 1, or if there are no more expressions. The result of a dfn is that of the last evaluated expression. If that last evaluated expression ends in assignment, the result is "shy"—not automatically displayed in the session.
Line 76 ⟶ 77:
== Examples ==
The examples here illustrate different aspects of dfns. Additional examples are found in the cited articles.<ref name=dfnsWS>{{citation|last=Scholes|first=John|title=Direct Functions Workspace|date=
=== Default left argument ===
The function {{code|{⍺+0j1×⍵}|apl}} adds {{code|⍺|apl}} to {{code|0j1|apl}} ({{mvar|i}} or
<
3 {⍺+0j1×⍵} 4
3J4
Line 90 ⟶ 91:
1J¯2 1J¯1 1 1J1 1J2
2J¯2 2J¯1 2 2J1 2J2
</syntaxhighlight>
The significance of this function can be seen as follows:
{{
Moreover, analogous to that monadic {{code|-⍵|apl}}
<
j←{⍺←0 ⋄ ⍺+0j1×⍵}
Line 113 ⟶ 114:
Euler (¯0.5+?10⍴0) j (¯0.5+?10⍴0)
1 1 1 1 1 1 1 1 1 1
</syntaxhighlight>
The last expression illustrates [[Euler's formula]] on ten random numbers with real and imaginary parts in the interval <math>\left(-0.5,0.5\right)</math>.
Line 129 ⟶ 130:
The Cantor set of order {{code|⍵|apl}} defined as a dfn:<ref name=Hui2016>{{citation|last=Hui|first=Roger|author-link=Roger Hui|title=APL Exercises|date=18 July 2016|url=http://www.jsoftware.com/papers/APL_exercises/|access-date=24 September 2019}}</ref>{{rp|§2.5}}
<
Cantor← {0=⍵:,1 ⋄ ,1 0 1 ∘.∧ ∇ ⍵-1}
Line 140 ⟶ 141:
Cantor 3
1 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 1
</syntaxhighlight>
Cantor 0 to Cantor 6 depicted as black bars:
Line 149 ⟶ 150:
The function {{code|sieve ⍵|apl}} computes a bit vector of length {{code|⍵|apl}} so that bit {{code|i|apl}} (for {{code|0≤i|apl}} and {{code|i<⍵|apl}}) is 1 if and only if {{code|i|apl}} is a [[Prime number|prime]].<ref name=history50/>{{rp|§46}}
<
sieve←{
4≥⍵:⍵⍴0 0 1 1
Line 176 ⟶ 177:
(10*⍳10) (+⌿↑)⍤0 1 ⊢b
0 4 25 168 1229 9592 78498 664579 5761455 50847534
</syntaxhighlight>
The last sequence, the number of primes less than powers of 10, is an initial segment of {{OEIS2C|id=A006880}}. The last number, 50847534, is the number of primes less than <math>10^9</math>. It is called Bertelsen's number, memorably described by [[MathWorld]] as "an erroneous name erroneously given the erroneous value of <math>\pi(10^9) = 50847478</math>".<ref name=MathWorld>{{citation|last=Weisstein|first=Eric W.|title=Bertelsen's Number|publisher=MathWorld, A Wolfram Web Resource|url=http://mathworld.wolfram.com/BertelsensNumber.html|access-date=26 September 2019}}</ref>
Line 183 ⟶ 184:
=== Tail recursion ===
Typically, the [[factorial]] function is define recursively (as [[#factorial|above]]), but it can be coded to exploit [[tail call|tail recursion]] by using an accumulator left argument:<ref name=fact>{{citation|last=Scholes|first=John|title=Factorial|date=
<
fac←{⍺←1 ⋄ ⍵=0:⍺ ⋄ (⍺×⍵) ∇ ⍵-1}
</syntaxhighlight>
Similarly, the [[determinant]] of a square complex matrix using [[Gaussian elimination]] can be computed with tail recursion:<ref name=det>{{citation|last=Scholes|first=John|title=Determinant|date=
<
det←{ ⍝ determinant of a square complex matrix
⍺←1 ⍝ product of co-factor coefficients so far
Line 197 ⟶ 198:
(⍺×⍵[i;j]ׯ1*i+j) ∇ ⍵[k~i;k~j] - ⍵[k~i;j] ∘.× ⍵[i;k~j]÷⍵[i;j]
}
</syntaxhighlight>
=== Multiple recursion ===
A [[
The [[Partition function (number theory)|partition function]] <math>P(n)</math> counts the number of partitions. The function is of interest in [[number theory]], studied by [[Leonhard Euler|Euler]], [[G. H. Hardy|Hardy]], [[Srinivasa Ramanujan|Ramanujan]], [[Paul Erdős|Erdős]], and others. The recurrence relation
Line 208 ⟶ 209:
derived from Euler's [[pentagonal number theorem]].<ref name=MathWorldP>{{citation|last=Weisstein|first=Eric W.|title=Partition Function P, equation 11|publisher=MathWorld, A Wolfram Web Resource|url=http://mathworld.wolfram.com/PartitionFunctionP.html|access-date=3 October 2019}}</ref> Written as a dfn:<ref name=history50/>{{rp|§16}}
<
pn ← {1≥⍵:0≤⍵ ⋄ -⌿+⌿∇¨rec ⍵}
rec ← {⍵ - (÷∘2 (×⍤1) ¯1 1 ∘.+ 3∘×) 1+⍳⌈0.5*⍨⍵×2÷3}
Line 216 ⟶ 217:
pn¨ ⍳13 ⍝ OEIS A000041
1 1 2 3 5 7 11 15 22 30 42 56 77
</syntaxhighlight>
The basis step {{code|1≥⍵:0≤⍵|apl}} states that for {{code|1≥⍵|apl}}, the result of the function is {{code|0≤⍵|apl}}, 1 if ⍵ is 0 or 1 and 0 otherwise. The recursive step is highly multiply recursive. For example, {{code|pn 200|apl}} would result in the function being applied to each element of {{code|rec 200|apl}}, which are:
<
rec 200
199 195 188 178 165 149 130 108 83 55 24 ¯10
198 193 185 174 160 143 123 100 74 45 13 ¯22
</syntaxhighlight>
and {{code|pn 200|apl}} requires longer than the [[age of the universe]] to compute (<math>7.57\times10^{47}</math> function calls to itself).<ref name=history50/>{{rp|§16}} The compute time can be reduced by [[memoization]], here implemented as the direct operator (higher-order function) {{code|M|apl}}:
<
M←{
f←⍺⍺
Line 239 ⟶ 240:
0 ⍕ pn M 200 ⍝ format to 0 decimal places
3972999029388
</syntaxhighlight>
This value of {{code|pn M 200|apl}} agrees with that computed by Hardy and Ramanujan in 1918.<ref>{{citation|last1=Hardy|first1=G.H.|last2=Ramanujan|first2=S.|title= Asymptotic Formulæ in Combinatory Analysis|journal=Proceedings of the London Mathematical Society|volume=17|number=2|date=1918|url=http://ramanujan.sirinudi.org/Volumes/published/ram36.pdf|access-date=24 December 2019}}</ref>
Line 245 ⟶ 246:
The memo operator {{code|M|apl}} defines a variant of its operand function {{code|⍺⍺|apl}} to use a [[Cache (computing)|cache]] {{code|T|apl}} and then evaluates it. With the operand {{code|pn|apl}} the variant is:
<
{T←(1+⍵)⍴¯1 ⋄ {1≥⍵:0≤⍵ ⋄ ¯1≢T[⍵]:⊃T[⍵] ⋄ ⊃T[⍵]←⊂-⌿+⌿∇¨rec ⍵}⍵}
</syntaxhighlight>
=== Direct operator (dop) ===
[[Quicksort]] on an array {{code|⍵|apl}} works by choosing a "pivot" at random among its major cells, then catenating the sorted major cells which strictly precede the pivot, the major cells equal to the pivot, and the sorted major cells which strictly follow the pivot, as determined by a comparison function {{code|⍺⍺|apl}}. Defined as a direct [[Higher-order function|operator]] (dop) {{code|Q|apl}}:
<
Q←{1≥≢⍵:⍵ ⋄ (∇ ⍵⌿⍨0>s)⍪(⍵⌿⍨0=s)⍪∇ ⍵⌿⍨0<s←⍵ ⍺⍺ ⍵⌷⍨?≢⍵}
Line 262 ⟶ 263:
(×-) Q x
0 2 3 3 4 6 7 8 9 10 14 15 19 19
</syntaxhighlight>
{{code|Q3|apl}} is a variant that catenates the three parts enclosed by the function {{code|⊂|apl}} instead of the parts ''per se''. The three parts generated at each recursive step are apparent in the structure of the final result. Applying the function derived from {{code|Q3|apl}} to the same argument multiple times gives different results because the pivots are chosen at random. [[Tree traversal#In-order,
<
Q3←{1≥≢⍵:⍵ ⋄ (⊂∇ ⍵⌿⍨0>s)⍪(⊂⍵⌿⍨0=s)⍪⊂∇ ⍵⌿⍨0<s←⍵ ⍺⍺ ⍵⌷⍨?≢⍵}
Line 294 ⟶ 295:
│└┴─┴──────────────────────┘│ │ │
└───────────────────────────┴─┴─────────────────────────────┘
</syntaxhighlight>
The above formulation is not new; see for example Figure 3.7 of the classic ''The Design and Analysis of Computer Algorithms''.<ref name=AHU>{{citation|
=== Dfns with operators and trains ===
Dfns, especially anonymous dfns, work well with operators and trains. The following snippet solves a "Programming Pearls" puzzle:<ref name=Bentley1983>{{cite journal|last=Bentley|first=Jon|title=Programming Pearls|journal=[[Communications of the ACM]]|volume=26|number=8 and 9|date=August 1983}}</ref> given a dictionary of English words, here represented as the character matrix {{code|a|apl}}, find all sets of anagrams.
<
a {⍵[⍋⍵]}⍤1 ⊢a ({⍵[⍋⍵]}⍤1 {⊂⍵}⌸ ⊢) a
pats apst ┌────┬────┬────┐
Line 316 ⟶ 317:
east aest
seta aest
</syntaxhighlight>
The algorithm works by sorting the rows individually ({{code|{⍵[⍋⍵]}⍤1 ⊢a|apl}}), and these sorted rows are used as keys ("signature" in the Programming Pearls description) to the ''key'' operator {{code|⌸|apl}} to group the rows of the matrix.<ref name=APL1978/>{{rp|§3.3}} The expression on the right is a ''train'', a syntactic form employed by APL to achieve [[tacit programming]]. Here, it is an isolated sequence of three functions such that {{code|(f g h) ⍵|apl}}
=== Lexical scope ===
Line 325 ⟶ 326:
For example, in the following function {{code|which|apl}}, the variable {{code|ty|apl}} is defined both in {{code|which|apl}} itself and in the inner function {{code|f1|apl}}. When {{code|f1|apl}} calls outward to {{code|f2|apl}} and {{code|f2|apl}} refers to {{code|ty|apl}}, it finds the outer one (with value {{code|'lexical'|apl}}) rather than the one defined in {{code|f1|apl}} (with value {{code|'dynamic'|apl}}):
<
which←{
ty←'lexical'
Line 335 ⟶ 336:
which ' scope'
lexical scope
</syntaxhighlight>
=== Error-guard ===
The following function illustrates use of error guards:<ref name=Dyalog17.1/>{{rp|p.139}}
<
plus←{
tx←'catch all' ⋄ 0::tx
Line 354 ⟶ 355:
2 3 plus 3 4⍴5 ⍝ can't add vector to matrix
catch all
</syntaxhighlight>
In APL, error number 5 is "length error"; error number 11 is "___domain error"; and error number 0 is a "catch all" for error numbers 1 to 999.
Line 364 ⟶ 365:
{| class="wikitable"
|-
| {{sxhl|1=
sieve←{
4≥⍵:⍵⍴0 0 1 1
Line 376 ⟶ 375:
{r<q←b⍳1:b⊣b[⍵]←1 ⋄ b[q,q×⍸b↑⍨⌈n÷q]←0 ⋄ ∇ ⍵,q}p
}
|2=apl}}
| {{sxhl|1=
∇ b←sieve1 n;i;m;p;q;r
:If 4≥n ⋄ b←n⍴0 0 1 1 ⋄ :Return ⋄ :EndIf
Line 397 ⟶ 388:
b[p]←1
∇
|2=apl}}
| {{sxhl|1=
∇ b←sieve2 n;i;m;p;q;r
→L10 ⍴⍨ 4<n ⋄ b←n⍴0 0 1 1 ⋄ →0
Line 420 ⟶ 406:
b[p]←1
∇
|2=apl}}
|}
Line 440 ⟶ 426:
== History ==
[[Kenneth E. Iverson]], the inventor of APL, was dissatisfied with the way user functions (tradfns) were defined. In 1974, he devised "formal function definition" or "direct definition" for use in exposition.<ref name=directdef>{{citation|last=Iverson|first=Kenneth E.|author-link=Kenneth E. Iverson|title=Elementary Functions|chapter=Chapter 10, Formal Function Definition|date=1974|publisher=IBM Corporation|chapter-url=http://www.jsoftware.com/papers/DirectDef.htm|access-date=18 September 2019}}</ref> A direct definition has two or four parts, separated by colons:
<
name : expression
name : expression0 : proposition : expression1
</syntaxhighlight>
Within a direct definition, {{code|⍺|apl}} denotes the left argument and {{code|⍵|apl}} the right argument. In the first instance, the result of {{code|expression|apl}} is the result of the function; in the second instance, the result of the function is that of {{code|expression0|apl}} if {{code|proposition|apl}} evaluates to 0, or {{code|expression1|apl}} if it evaluates to 1. Assignments within a direct definition are [[Scope (computer science)#Dynamic scoping|dynamically local]]. Examples of using direct definition are found in the 1979 [[Turing Award]] Lecture<ref name=TOT>{{cite journal|last=Iverson|first=Kenneth E.|author-link=Kenneth E. Iverson|title=Notation as a Tool of Thought|journal=[[Communications of the ACM]]|volume=23|number=8|date=August 1980|url=https://www.jsoftware.com/papers/tot.htm|access-date=8 April 2016|doi=10.1145/358896.358899|pages=444–465|doi-access=free}}</ref> and in books and application papers.<ref name=Iverson1976>{{cite book|last=Iverson|first=Kenneth E.|author-link=Kenneth E. Iverson|title=Elementary Analysis|publisher=APL Press|date=1976}}</ref><ref name=Orth1976>{{cite book|last=Orth|first=D.L.|title=Calculus in a New Key|publisher=APL Press|date=1976}}</ref><ref name=Hui1987>{{cite journal|last=Hui|first=Roger|author-link=Roger Hui|title=Some Uses of { and }|journal=APL 87 Conference Proceedings|date=May 1987|url=http://www.jsoftware.com/papers/from.htm|access-date=15 April 2016}}</ref><ref name=McDonnell1987>{{citation|last=McDonnell|first=E.E.|title=Life: Nasty, Brutish, and Short|journal=APL 87 Conference Proceedings|date=May 1987|url=http://www.jsoftware/papers/EEM/life.htm|access-date=6 October 2019}}{{Dead link|date=May 2023 |bot=InternetArchiveBot |fix-attempted=yes }}</ref><ref name=APL1978>{{
Direct definition was too limited for use in larger systems. The ideas were further developed by multiple authors in multiple works<ref name=opfns>{{citation|last=Iverson|first=Kenneth E.|author-link=Kenneth E. Iverson|title=Operators and Functions|journal=Research Report Number #RC7091|date=26 April 1978|publisher=IBM Corporation|url=http://www.jsoftware.com/papers/opfns.htm|access-date=2019-09-19}}</ref>{{rp|§8}}<ref name=IversonWooster1981>{{cite journal|
In 1996, [[John M. Scholes|John Scholes]] of Dyalog Limited invented direct functions (dfns).<ref name=Scholes1996/><ref name=Scholes2018v/><ref name=Scholes2018t/> The ideas originated in 1989 when he read a special issue of ''[[The Computer Journal]]'' on functional programming.<ref name=Wadler>{{cite journal|last=Wadler|first=Philip L.|display-authors=etal|title=Special Issue on Functional Programming|journal=[[The Computer Journal]]|volume=32|number=2|date=1 January 1989}}</ref> He then proceeded to study functional programming and became strongly motivated ("sick with desire", like [[W. B. Yeats|Yeats]]) to bring these ideas to APL.<ref name=Scholes2018v/><ref name=Scholes2018t/> He initially operated in stealth because he was concerned the changes might be judged too radical and an unnecessary complication of the language; other observers say that he operated in stealth because Dyalog colleagues were not so enamored and thought he was wasting his time and causing trouble for people. Dfns were first presented in the Dyalog Vendor Forum at the APL '96 Conference and released in Dyalog APL in early 1997.<ref name=Scholes1996/> Acceptance and recognition were slow in coming. As late as 2008, in ''Dyalog at 25'',<ref name=Dyalog@25>{{cite journal|last=Dyalog|title=Dyalog at 25|journal=Vector|date=September 2008|url=http://www.dyalog.com/uploads/documents/dyalog_25.pdf|access-date=2019-09-20}}</ref> a publication celebrating the 25th anniversary of Dyalog Limited, dfns were barely mentioned (mentioned twice as "dynamic functions" and without elaboration).
== References ==
|