Fonction directe - Direct function
Une fonction directe ( dfn , prononcé « dee fun ») est une autre façon de définir une fonction et un opérateur (une fonction d'ordre supérieur ) dans le langage de programmation APL . Un opérateur direct peut également être appelé un dop (prononcé "dee op"). Ils ont été inventés par John Scholes en 1996. Ils sont une combinaison unique de programmation matricielle , de fonction d'ordre supérieur et de programmation fonctionnelle , et constituent une avancée majeure de l'APL du début du 21e siècle par rapport aux versions précédentes.
Un dfn est une séquence d' expressions éventuellement gardées (ou juste une garde) entre {et }, séparées par ⋄ou des nouvelles lignes, où ⍺dénote l'argument gauche et ⍵le droit, et ∇dénote la récursivité (fonction auto-référence). Par exemple, la fonction PTteste si chaque ligne de ⍵est un triplet pythagoricien (en testant si la somme des carrés est égale à deux fois le carré du maximum).
PT← {(+/⍵*2)=2×(⌈/⍵)*2}
PT 3 4 5
1
x
4 5 3
3 11 6
5 13 12
17 16 8
11 12 4
17 15 8
PT x
1 0 1 0 0 1
La fonction factorielle en tant que dfn :
fact← {0=⍵:1 ⋄ ⍵×∇ ⍵-1}
fact 5
120
fact¨ ⍳10 ⍝ fact applied to each element of 0 to 9
1 1 2 6 24 120 720 5040 40320 362880
La description
Les règles de dfns sont résumées par la "carte de référence" suivante :
{⍺ function ⍵}
|
{⍺⍺ operator ⍵⍵}
|
: gardien
|
⍺ argument de gauche
|
⍺⍺ opérande gauche
|
:: erreur-garde
|
⍵ bon argument
|
⍵⍵ opérande droit
|
⍺← argument gauche par défaut
|
∇ auto-référence
|
∇∇ auto-référence
|
s← résultat timide
|
Un dfn est une séquence d' expressions éventuellement gardées (ou juste une garde) entre {et }, séparées par ⋄ou des retours à la ligne.
expression
guard: expression
guard:
Les expressions et/ou gardes sont évaluées en séquence. Un gardien doit évaluer à 0 ou 1 ; son expression associée est évaluée si la valeur est 1. Un dfn se termine après la première expression non gardée qui ne se termine pas par affectation , ou après la première expression gardée dont la garde vaut 1, ou s'il n'y a plus d'expressions. Le résultat d'un dfn est celui de la dernière expression évaluée. Si cette dernière expression évaluée se termine par une affectation, le résultat est « timide » et n'est pas automatiquement affiché dans la session.
Les noms attribués dans un dfn sont locaux par défaut, avec une portée lexicale .
⍺désigne l'argument de fonction gauche et ⍵le droit; ⍺⍺désigne l'opérande gauche et ⍵⍵la droite. Si ⍵⍵apparaît dans la définition, alors le dfn est un opérateur dyadique ; si seulement ⍺⍺se produit mais pas ⍵⍵, alors c'est un opérateur monadique ; si ni ⍺⍺ou ⍵⍵se produit, alors le dfn est une fonction.
La syntaxe spéciale est utilisée pour donner une valeur par défaut à l'argument gauche si un dfn est appelé monadiquement, c'est-à-dire appelé sans argument gauche. Le n'est pas évalué autrement.
⍺←expression⍺←expression
∇désigne la récursivité ou l'auto-référence par la fonction, et l' ∇∇auto-référence par l'opérateur. Une telle dénotation permet une récursivité anonyme .
Le piégeage des erreurs est assuré par des protections contre les erreurs, . Lorsqu'une erreur est générée, le système recherche dynamiquement via les fonctions appelantes une protection contre les erreurs qui correspond à l'erreur. Si un est trouvé, l'environnement d'exécution est déroulé dans son état immédiatement avant l'exécution de la protection contre les erreurs et l'expression associée de la protection contre les erreurs est évaluée comme le résultat du dfn.
errnums::expression
Des descriptions supplémentaires, des explications et des tutoriels sur les dfns sont disponibles dans les articles cités.
Exemples
Les exemples ici illustrent différents aspects de dfns. Des exemples supplémentaires se trouvent dans les articles cités.
Argument gauche par défaut
La fonction ajoute à ( i ou √ −1 ) fois .
{⍺+0j1×⍵}⍺0j1⍵
3 {⍺+0j1×⍵} 4
3J4
∘.{⍺+0j1×⍵}⍨ ¯2+⍳5
¯2J¯2 ¯2J¯1 ¯2 ¯2J1 ¯2J2
¯1J¯2 ¯1J¯1 ¯1 ¯1J1 ¯1J2
0J¯2 0J¯1 0 0J1 0J2
1J¯2 1J¯1 1 1J1 1J2
2J¯2 2J¯1 2 2J1 2J2
La signification de cette fonction peut être vue comme suit :
Les nombres complexes peuvent être construits comme des paires ordonnées de nombres réels, de la même manière que les nombres entiers peuvent être construits comme des paires ordonnées de nombres naturels et les nombres rationnels comme des paires ordonnées d'entiers. Pour les nombres complexes, joue le même rôle que pour les nombres entiers et pour les nombres rationnels.
{⍺+0j1×⍵}-÷
De plus, analogue à ce monadique ⇔ ( négation ) et monadique ⇔ ( réciproque ), une définition monadique de la fonction est utile, effectuée en spécifiant une valeur par défaut de 0 pour : si , alors ⇔ ⇔ .
-⍵0-⍵÷⍵1÷⍵⍺j←{⍺←0 ⋄ ⍺+0j1×⍵}j ⍵0 j ⍵0+0j1×⍵
j←{⍺←0 ⋄ ⍺+0j1×⍵}
3 j 4 ¯5.6 7.89
3J4 3J¯5.6 3J7.89
j 4 ¯5.6 7.89
0J4 0J¯5.6 0J7.89
sin← 1∘○
cos← 2∘○
Euler← {(*j ⍵) = (cos ⍵) j (sin ⍵)}
Euler (¯0.5+?10⍴0) j (¯0.5+?10⍴0)
1 1 1 1 1 1 1 1 1 1
La dernière expression illustre la formule d'Euler sur dix nombres aléatoires avec des parties réelles et imaginaires dans l'intervalle .
Récursivité unique
La construction ternaire de l' ensemble de Cantor commence par l'intervalle [0,1] et à chaque étape supprime le tiers médian de chaque sous-intervalle restant :
L'ensemble de commande Cantor ⍵défini comme un dfn :
Cantor← {0=⍵:,1 ⋄ ,1 0 1 ∘.∧ ∇ ⍵-1}
Cantor 0
1
Cantor 1
1 0 1
Cantor 2
1 0 1 0 0 0 1 0 1
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
Cantor 0 à Cantor 6 représentés par des barres noires :
La fonction calcule un vecteur de bits de longueur de sorte que le bit (pour et ) vaut 1 si et seulement si est un nombre premier .
sieve ⍵⍵i0≤ii<⍵i
sieve←{
4≥⍵:⍵⍴0 0 1 1
r←⌊0.5*⍨n←⍵
p←2 3 5 7 11 13 17 19 23 29 31 37 41 43
p←(1+(n≤×⍀p)⍳1)↑p
b← 0@1 ⊃ {(m⍴⍵)>m⍴⍺↑1 ⊣ m←n⌊⍺×≢⍵}⌿ ⊖1,p
{r<q←b⍳1:b⊣b[⍵]←1 ⋄ b[q,q×⍸b↑⍨⌈n÷q]←0 ⋄ ∇ ⍵,q}p
}
10 10 ⍴ sieve 100
0 0 1 1 0 1 0 1 0 0
0 1 0 1 0 0 0 1 0 1
0 0 0 1 0 0 0 0 0 1
0 1 0 0 0 0 0 1 0 0
0 1 0 1 0 0 0 1 0 0
0 0 0 1 0 0 0 0 0 1
0 1 0 0 0 0 0 1 0 0
0 1 0 1 0 0 0 0 0 1
0 0 0 1 0 0 0 0 0 1
0 0 0 0 0 0 0 1 0 0
b←sieve 1e9
≢b
1000000000
(10*⍳10) (+⌿↑)⍤0 1 ⊢b
0 4 25 168 1229 9592 78498 664579 5761455 50847534
La dernière séquence, le nombre de nombres premiers inférieurs aux puissances de 10, est un segment initial de OEIS : A006880 . Le dernier nombre, 50847534, est le nombre de nombres premiers inférieur à . Il s'agit du nombre de Bertelsen, décrit de manière mémorable par MathWorld comme "un nom erroné donné à tort la valeur erronée de ".
sieveutilise deux méthodes différentes pour les composites de marque avec 0s, à la fois effectuée en utilisant DFNS anonymes locales: Les premières utilisations du tamis de Eratosthènes sur un masque initial de 1 et un préfixe des nombres premiers 2 3 ... 43, en utilisant l' insert opérateur ⌿( replient droit ). (La longueur du préfixe est obtenue par comparaison avec la fonction primordiale .) Le second trouve le plus petit nouveau nombre premier restant dans ( ), et met à 0 bit lui-même et bits parfois les nombres à 1 bits restants dans un segment initial de ( ) . Ce deuxième dfn utilise la récursivité de la queue.
×⍀pqbq←b⍳1qqb⍸b↑⍨⌈n÷q
Récursivité de la queue
Typiquement, la fonction factorielle est définie de manière récursive (comme ci-dessus ), mais elle peut être codée pour exploiter la récursivité de la queue en utilisant un argument gauche d'accumulateur :
fac←{⍺←1 ⋄ ⍵=0:⍺ ⋄ (⍺×⍵) ∇ ⍵-1}
De même, le déterminant d'une matrice carrée complexe utilisant l'élimination gaussienne peut être calculé avec la récursivité de queue :
det←{ ⍝ determinant of a square complex matrix
⍺←1 ⍝ product of co-factor coefficients so far
0=≢⍵:⍺ ⍝ result for 0-by-0
(i j)←(⍴⍵)⊤⊃⍒|,⍵ ⍝ row and column index of the maximal element
k←⍳≢⍵
(⍺×⍵[i;j]ׯ1*i+j) ∇ ⍵[k~i;k~j] - ⍵[k~i;j] ∘.× ⍵[i;k~j]÷⍵[i;j]
}
Récursivité multiple
Une partition d'un entier non négatif est un vecteur d'entiers positifs tels que , où l'ordre dans n'est pas significatif. Par exemple, et sont des partitions de 4, et et et sont considérés comme la même partition.
n = +⌿v2 22 1 12 1 11 2 11 1 2
La fonction de partition compte le nombre de partitions. La fonction présente un intérêt pour la théorie des nombres , étudiée par Euler , Hardy , Ramanujan , Erdős et d'autres. La relation de récurrence
dérivé du théorème des nombres pentagonaux d'Euler . Écrit en dfn :
pn ← {1≥⍵:0≤⍵ ⋄ -⌿+⌿∇¨rec ⍵}
rec ← {⍵ - (÷∘2 (×⍤1) ¯1 1 ∘.+ 3∘×) 1+⍳⌈0.5*⍨⍵×2÷3}
pn 10
42
pn¨ ⍳13 ⍝ OEIS A000041
1 1 2 3 5 7 11 15 22 30 42 56 77
L'étape de base indique que pour , le résultat de la fonction est , 1 si ⍵ est 0 ou 1 et 0 sinon. L'étape récursive est hautement récursive multipliée. Par exemple, la fonction serait appliquée à chaque élément de , qui sont :
1≥⍵:0≤⍵1≥⍵0≤⍵pn 200rec 200
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
et nécessite plus de temps que l' âge de l'univers pour calculer (la fonction s'appelle elle-même). Le temps de calcul peut être réduit par memoization , ici implémenté comme l'opérateur direct (fonction d'ordre supérieur) :
pn 200M
M←{
f←⍺⍺
i←2+'⋄'⍳⍨t←2↓,⎕cr 'f'
⍎'{T←(1+⍵)⍴¯1 ⋄ ',(i↑t),'¯1≢T[⍵]:⊃T[⍵] ⋄ ⊃T[⍵]←⊂',(i↓t),'⍵}⍵'
}
pn M 200
3.973E12
0 ⍕ pn M 200 ⍝ format to 0 decimal places
3972999029388
Cette valeur de concorde avec celle calculée par Hardy et Ramanujan en 1918.
pn M 200
L'opérateur mémo Mdéfinit une variante de sa fonction opérande ⍺⍺pour utiliser un cache T , puis l'évalue. Avec l'opérande pnla variante est :
{T←(1+⍵)⍴¯1 ⋄ {1≥⍵:0≤⍵ ⋄ ¯1≢T[⍵]:⊃T[⍵] ⋄ ⊃T[⍵]←⊂-⌿+⌿∇¨rec ⍵}⍵}
Opérateur direct (dop)
Le tri rapide sur un tableau ⍵fonctionne en choisissant un "pivot" au hasard parmi ses cellules principales, puis en caténant les cellules principales triées qui précèdent strictement le pivot, les cellules principales égales au pivot et les cellules principales triées qui suivent strictement le pivot, comme déterminé par une fonction de comparaison ⍺⍺. Défini comme un opérateur direct (dop) Q:
Q←{1≥≢⍵:⍵ ⋄ (∇ ⍵⌿⍨0>s)⍪(⍵⌿⍨0=s)⍪∇ ⍵⌿⍨0<s←⍵ ⍺⍺ ⍵⌷⍨?≢⍵}
⍝ precedes ⍝ follows ⍝ equals
2 (×-) 8 8 (×-) 2 8 (×-) 8
¯1 1 0
x← 2 19 3 8 3 6 9 4 19 7 0 10 15 14
(×-) Q x
0 2 3 3 4 6 7 8 9 10 14 15 19 19
Q3est une variante qui enchaîne les trois parties délimitées par la fonction ⊂au lieu des parties elles-mêmes . Les trois parties générées à chaque étape récursive sont apparentes dans la structure du résultat final. L'application de la fonction dérivée de Q3au même argument plusieurs fois donne des résultats différents car les pivots sont choisis au hasard. La traversée dans l'ordre des résultats donne le même tableau trié.
Q3←{1≥≢⍵:⍵ ⋄ (⊂∇ ⍵⌿⍨0>s)⍪(⊂⍵⌿⍨0=s)⍪⊂∇ ⍵⌿⍨0<s←⍵ ⍺⍺ ⍵⌷⍨?≢⍵}
(×-) Q3 x
┌────────────────────────────────────────────┬─────┬┐
│┌──────────────┬─┬─────────────────────────┐│19 19││
││┌──────┬───┬─┐│6│┌──────┬─┬──────────────┐││ ││
│││┌┬─┬─┐│3 3│4││ ││┌┬─┬─┐│9│┌┬──┬────────┐│││ ││
│││││0│2││ │ ││ ││││7│8││ │││10│┌──┬──┬┐││││ ││
│││└┴─┴─┘│ │ ││ ││└┴─┴─┘│ │││ ││14│15││││││ ││
││└──────┴───┴─┘│ ││ │ │││ │└──┴──┴┘││││ ││
││ │ ││ │ │└┴──┴────────┘│││ ││
││ │ │└──────┴─┴──────────────┘││ ││
│└──────────────┴─┴─────────────────────────┘│ ││
└────────────────────────────────────────────┴─────┴┘
(×-) Q3 x
┌───────────────────────────┬─┬─────────────────────────────┐
│┌┬─┬──────────────────────┐│7│┌────────────────────┬─────┬┐│
│││0│┌┬─┬─────────────────┐││ ││┌──────┬──┬────────┐│19 19│││
│││ │││2│┌────────────┬─┬┐│││ │││┌┬─┬─┐│10│┌──┬──┬┐││ │││
│││ │││ ││┌───────┬─┬┐│6│││││ │││││8│9││ ││14│15││││ │││
│││ │││ │││┌┬───┬┐│4│││ │││││ │││└┴─┴─┘│ │└──┴──┴┘││ │││
│││ │││ │││││3 3│││ │││ │││││ ││└──────┴──┴────────┘│ │││
│││ │││ │││└┴───┴┘│ │││ │││││ │└────────────────────┴─────┴┘│
│││ │││ ││└───────┴─┴┘│ │││││ │ │
│││ │││ │└────────────┴─┴┘│││ │ │
│││ │└┴─┴─────────────────┘││ │ │
│└┴─┴──────────────────────┘│ │ │
└───────────────────────────┴─┴─────────────────────────────┘
La formulation ci-dessus n'est pas nouvelle; voir par exemple la figure 3.7 du classique The Design and Analysis of Computer Algorithms . Cependant, contrairement au programme pidgin ALGOL de la figure 3.7, il Qest exécutable et l'ordre partiel utilisé dans le tri est un opérande, comme les exemples ci-dessus.
(×-)
Dfns avec opérateurs et trains
Les Dfns, en particulier les DFN anonymes, fonctionnent bien avec les opérateurs et les trains. L'extrait suivant résout un casse-tête "Programming Pearls" : à partir d'un dictionnaire de mots anglais, représenté ici par la matrice de caractères a, trouvez tous les ensembles d'anagrammes.
a {⍵[⍋⍵]}⍤1 ⊢a ({⍵[⍋⍵]}⍤1 {⊂⍵}⌸ ⊢) a
pats apst ┌────┬────┬────┐
spat apst │pats│teas│star│
teas aest │spat│sate│ │
sate aest │taps│etas│ │
taps apst │past│seat│ │
etas aest │ │eats│ │
past apst │ │tase│ │
seat aest │ │east│ │
eats aest │ │seta│ │
tase aest └────┴────┴────┘
star arst
east aest
seta aest
L'algorithme fonctionne en triant les lignes individuellement ( ), et ces lignes triées sont utilisées comme clés ("signature" dans la description de Programming Pearls) à l' opérateur clé pour regrouper les lignes de la matrice. L'expression à droite est un train , une forme syntaxique employée par APL pour réaliser une programmation tacite . Ici, il est une séquence isolée de trois fonctions telles que ⇔ , d' où l'expression de droite est équivalente à .
{⍵[⍋⍵]}⍤1 ⊢a⌸(f g h) ⍵(f ⍵) g (h ⍵)({⍵[⍋⍵]}⍤1 ⊢a) {⊂⍵}⌸ a
Portée lexicale
Lorsqu'un dfn interne (imbriqué) fait référence à un nom, il est recherché en regardant vers l'extérieur à travers des dfns englobants plutôt que vers le bas de la pile d'appels . On dit que ce régime emploie la portée lexicale au lieu de la portée dynamique habituelle de l'APL . La distinction ne devient apparente que si un appel est fait à une fonction définie à un niveau externe. Pour les appels entrants plus habituels, les deux régimes sont indiscernables.
Par exemple, dans la fonction suivante which, la variable tyest définie à la fois en whichelle-même et dans la fonction interne f1. Lorsqu'il f1appelle vers f2et se f2réfère à ty, il trouve l'externe (avec la valeur 'lexical') plutôt que celui défini dans f1(avec la valeur 'dynamic'):
which←{
ty←'lexical'
f1←{ty←'dynamic' ⋄ f2 ⍵}
f2←{ty,⍵}
f1 ⍵
}
which ' scope'
lexical scope
Erreur-garde
La fonction suivante illustre l'utilisation des protections contre les erreurs :
plus←{
tx←'catch all' ⋄ 0::tx
tx←'domain' ⋄ 11::tx
tx←'length' ⋄ 5::tx
⍺+⍵
}
2 plus 3 ⍝ no errors
5
2 3 4 5 plus 'three' ⍝ argument lengths don't match
length
2 3 4 5 plus 'four' ⍝ can't add characters
domain
2 3 plus 3 4⍴5 ⍝ can't add vector to matrix
catch all
En APL, l'erreur numéro 5 est « erreur de longueur » ; le numéro d'erreur 11 est « erreur de domaine » ; et le numéro d'erreur 0 est un "attrape tout" pour les numéros d'erreur 1 à 999.
L'exemple montre le déroulement de l'environnement local avant l'évaluation d'une expression de protection contre les erreurs. Le nom local txest défini pour décrire la portée de sa protection contre les erreurs suivante. Lorsqu'une erreur se produit, l'environnement est déroulé pour exposer txla valeur statiquement correcte de .
Dfns contre tradfns
Les fonctions directes étant des dfns, les fonctions APL définies de manière traditionnelle sont appelées tradfns, prononcées « trad funs ». Ici, dfns et tradfns sont comparés en considérant la fonction sieve: A gauche se trouve un dfn (tel que défini ci-dessus ); au milieu se trouve un tradfn utilisant des structures de contrôle ; à droite se trouve un tradfn utilisant des gotos ( →) et des étiquettes de ligne .
sieve←{
4≥⍵:⍵⍴0 0 1 1
r←⌊0.5*⍨n←⍵
p←2 3 5 7 11 13 17 19 23 29 31 37 41 43
p←(1+(n≤×⍀p)⍳1)↑p
b← 0@1 ⊃ {(m⍴⍵)>m⍴⍺↑1 ⊣ m←n⌊⍺×≢⍵}⌿ ⊖1,p
{r<q←b⍳1:b⊣b[⍵]←1 ⋄ b[q,q×⍸b↑⍨⌈n÷q]←0 ⋄ ∇ ⍵,q}p
}
|
∇ b←sieve1 n;i;m;p;q;r :If 4≥n ⋄ b←n⍴0 0 1 1 ⋄ :Return ⋄ :EndIf r←⌊0.5*⍨n p←2 3 5 7 11 13 17 19 23 29 31 37 41 43 p←(1+(n≤×⍀p)⍳1)↑p b←1 :For q :In p ⋄ b←(m⍴b)>m⍴q↑1 ⊣ m←n⌊q×≢b ⋄ :EndFor b[1]←0 :While r≥q←b⍳1 ⋄ b[q,q×⍸b↑⍨⌈n÷q]←0 ⋄ p⍪←q ⋄ :EndWhile b[p]←1 ∇ |
∇ b←sieve2 n;i;m;p;q;r →L10 ⍴⍨ 4<n ⋄ b←n⍴0 0 1 1 ⋄ →0 L10: r←⌊0.5*⍨n p←2 3 5 7 11 13 17 19 23 29 31 37 41 43 p←(1+(n≤×\p)⍳1)↑p i←0 ⋄ b←1 L20: b←(m⍴b)>m⍴p[i]↑1 ⊣ m←n⌊p[i]×≢b →L20 ⍴⍨ (≢p)>i←1+i b[1]←0 L30: →L40 ⍴⍨ r<q←b⍳1 ⋄ b[q,q×⍸b↑⍨⌈n÷q]←0 ⋄ p⍪←q ⋄ →L30 L40: b[p]←1 ∇ |
- Un dfn peut être anonyme ; un tradfn doit être nommé.
- Un dfn est nommé par affectation (
←); un tradfn est nommé en incorporant le nom dans la représentation de la fonction et en appliquant⎕fx(une fonction système) à cette représentation. - Un dfn est plus pratique qu'un tradfn en tant qu'opérande (voir les éléments précédents : un tradfn doit être nommé ; un tradfn est nommé par incorporation...).
- Les noms attribués dans un dfn sont locaux par défaut ; les noms attribués dans un tradfn sont globaux à moins qu'ils ne soient spécifiés dans une liste locale.
- Les locaux dans un dfn ont une portée lexicale ; les locaux dans un tradfn ont une portée dynamique , visible dans les fonctions appelées à moins d'être masquées par leur liste de locaux.
- Les arguments d'un dfn sont nommés
⍺et⍵et les opérandes d'un dop sont nommés⍺⍺et⍵⍵; les arguments et opérandes d'un tradfn peuvent avoir n'importe quel nom, spécifié sur sa première ligne. - Le résultat (le cas échéant) d'un dfn n'est pas nommé ; le résultat (le cas échéant) d'un tradfn est nommé dans son en-tête.
- Une valeur par défaut pour est spécifiée plus précisément que pour l'argument gauche d'un tradfn.
-
La récursivité dans un dfn est effectuée en invoquant
∇ou∇∇ou son nom ; la récursivité dans un tradfn s'effectue en invoquant son nom. -
Le contrôle de flux dans un dfn est effectué par des gardes et des appels de fonction ; que dans un tradfn est par des structures de contrôle et
→(goto) et des étiquettes de ligne. - L'évaluation d'une expression dans un dfn ne se terminant pas par une affectation provoque le retour du dfn ; l'évaluation d'une ligne dans un tradfn ne se terminant pas par une affectation ou goto affiche le résultat de la ligne.
- Un dfn revient sur l'évaluation d'une expression ne se terminant pas par une affectation, sur l'évaluation d'une expression protégée ou après la dernière expression ; un tradfn retourne sur
→(goto) la ligne 0 ou une ligne inexistante , ou sur l'évaluation d'une structure de contrôle, ou après la dernière ligne.:Return - Le contrôle de flux plus simple dans un dfn facilite la détection et la mise en œuvre de la récursivité de queue que dans un tradfn.
- Un dfn peut appeler un tradfn et vice versa ; un dfn peut être défini dans un tradfn, et vice versa .
Histoire
Kenneth E. Iverson , l'inventeur de l'APL, n'était pas satisfait de la façon dont les fonctions utilisateur (tradfns) étaient définies. En 1974, il a conçu une "définition de fonction formelle" ou une "définition directe" à utiliser dans l'exposition. Une définition directe comporte deux ou quatre parties, séparées par des deux-points :
name : expression
name : expression0 : proposition : expression1
Dans une définition directe, ⍺désigne l'argument gauche et ⍵l'argument droit. Dans le premier cas, le résultat de expressionest le résultat de la fonction ; dans le second cas, le résultat de la fonction est celui de expression0if propositions'évalue à 0, ou expression1s'il s'évalue à 1. Les affectations au sein d'une définition directe sont dynamiquement locales . Des exemples d'utilisation de la définition directe se trouvent dans la conférence Turing Award 1979 et dans des livres et des documents d'application.
La définition directe était trop limitée pour être utilisée dans des systèmes plus importants. Les idées ont été développées par plusieurs auteurs dans plusieurs travaux, mais les résultats étaient difficiles à manier. Parmi celles-ci, la "définition alternative de la fonction APL" de Bunda en 1987 se rapproche le plus des installations actuelles, mais présente des défauts en termes de conflits avec les symboles existants et de gestion des erreurs qui auraient causé des difficultés pratiques, et n'a jamais été implémentée. Les principaux distillats des différentes propositions étaient que (a) la fonction définie est anonyme, la dénomination ultérieure (si nécessaire) étant effectuée par affectation ; (b) la fonction est indiquée par un symbole et permet ainsi une récursivité anonyme .
En 1996, John Scholes de Dyalog Limited a inventé les fonctions directes (dfns). Les idées sont nées en 1989 lorsqu'il a lu un numéro spécial de The Computer Journal sur la programmation fonctionnelle. Il a ensuite étudié la programmation fonctionnelle et est devenu fortement motivé ("malade de désir", comme Yeats ) pour apporter ces idées à APL. Il a d'abord opéré discrètement parce qu'il craignait que les changements soient jugés trop radicaux et une complication inutile de la langue ; d'autres observateurs disent qu'il a opéré en cachette parce que les collègues de Dyalog n'étaient pas si amoureux et pensaient qu'il perdait son temps et causait des problèmes aux gens. Dfns a été présenté pour la première fois au Dyalog Vendor Forum lors de la conférence APL '96 et publié dans Dyalog APL au début de 1997. L'acceptation et la reconnaissance ont été lentes à venir. Jusqu'en 2008, dans Dyalog at 25 , une publication célébrant le 25e anniversaire de Dyalog Limited, les dfns étaient à peine mentionnés (mentionnés deux fois comme "fonctions dynamiques" et sans élaboration). Depuis 2019, les dfns sont implémentés dans Dyalog APL, NARS2000 et ngn/apl. Ils jouent également un rôle clé dans les efforts visant à exploiter les capacités de calcul d'une unité de traitement graphique (GPU).
Les références
Liens externes
- Site officiel , Dyalog