A Train is a sequence of 2 or 3 items in an expression which bind together to form a function. Each item in a train may be an array or a function but the right-most item must be a function.
The following trains are currently supported where f, g and h are functions and A is an array:
f g h
A g h
g h
The 3-item trains (f g h) and (A g h) are termed forks while the 2-item train (g h) is termed an atop. To distinguish the two styles of fork, we can use the terms fgh-fork or Agh-fork.
A train is syntactically equivalent to a function and so, in common with any other function, may be:
In particular, trains may be applied to a single array (monadic use) or between 2 arrays (dyadic use), providing six new constructs.
⍺(f g h)⍵ ←→ (⍺ f ⍵) g (⍺ h ⍵) ⍝ dyadic (fgh) fork ⍺(A g h)⍵ ←→ A g (⍺ h ⍵) ⍝ dyadic (Agh) fork ⍺( g h)⍵ ←→ g (⍺ h ⍵) ⍝ dyadic atop (f g h)⍵ ←→ ( f ⍵) g ( h ⍵) ⍝ monadic (fgh) fork (A g h)⍵ ←→ A g ( h ⍵) ⍝ monadic (Agh) fork ( g h)⍵ ←→ g ( h ⍵) ⍝ monadic atop
For a sequence to be interpreted as a train it must be separated from the argument to which it is applied. This can be done using parentheses or by naming the derived function.
(-,÷)5 ̄5 0.2
negrec←-,÷ negrec 5 ̄5 0.2
Whereas, without these means to identify the sequence as a train, the expression:
-,÷ 5 ̄0.2
means the negation of the ravel of the reciprocal of 5.
Function trains lend themselves to idiom recognition, a technique used to optimise the performance of certain expressions.
An expression to find the first position in a random integer vector X of a number greater than 999000 is:
X←?1e6⍴1e6 (X≥999000)⍳1 1704
A function train is not only more concise, it is faster too.
X (⍳∘1 ≥) 999000 1704
As a train resolves to a function, a sequences of more than 3 functions represents a train of trains. Function sequences longer than 3 are bound in threes, starting from the right:
... fu fv fw fx fy fz → ... fu (fv fw (fx fy fz))
This means that, in the absence of parentheses, a sequence of an odd number of functions resolves to a 3-train (fork) and an even-numbered sequence resolves to a 2-train (atop):
e f g h i j k → e f(g h(i j k)) ⍝ fork(fork(fork))
f g h i j k → f(g h(i j k)) ⍝ atop(fork(fork))
6( ×ばつ,÷)2 ⍝ fork:(6+2),((6-2),((×ばつ2),(6÷2))) 8 4 12 3 6(×ばつ,÷)2 ⍝ atop: ⌽ (6+2), ... 3 12 4 8 ]boxing on Was OFF
×ばつ,÷ ⍝ boxed display of fork ┌─┬─┬─────────────┐ │+│,│┌─┬─┬───────┐│ │ │ ││-│,│┌─┬─┬─┐││ │ │ ││ │ ×ばつ│,│÷│││ │ │ ││ │ │└─┴─┴─┘││ │ │ │└─┴─┴───────┘│ └─┴─┴─────────────┘ ×ばつ,÷ ⍝ boxed display of atop ┌─┬───────────────────┐ │⌽│┌─┬─┬─────────────┐│ │ ││+│,│┌─┬─┬───────┐││ │ ││ │ ││-│,│┌─┬─┬─┐│││ │ ││ │ ││ │ ×ばつ│,│÷││││ │ ││ │ ││ │ │└─┴─┴─┘│││ │ ││ │ │└─┴─┴───────┘││ │ │└─┴─┴─────────────┘│ └─┴───────────────────┘
]boxing -trains=tree Was -trains=box ×ばつ,÷ ⍝ boxed (tree) display of fork ┌─┼───┐ + , ┌─┼───┐ - , ┌─┼─┐ ×ばつ , ÷
The binding strength between the items of a train is less than that of operand-operator binding. In other words, operators bind first with their function (or array) operands to form derived functions, which may then participate as items in a train.
+⌿ ÷ ≢ ⍝ fork for mean value ┌─────┬─┬─┐ │┌─┬─┐│÷│≢│ ││+│⌿││ │ │ │└─┴─┘│ │ │ └─────┴─┴─┘ ⌊/,⌈/ ⍝ fork for min_max ┌─────┬─┬─────┐ │┌─┬─┐│,│┌─┬─┐│ ││⌊│/││ ││⌈│/││ │└─┴─┘│ │└─┴─┘│ └─────┴─┴─────┘
This means that any of the four hybrid tokens / ⌿ \ ⍀ will not be interpreted as a function if there's a function to its left in the train. In order to fix one of these tokens as a replicate or expand function, it must be isolated from the function to its left:
(⍳/⍳)3 ⍝ → ⍳/ atop ⍳3 → RANK ERROR
RANK ERROR
(⍳{⍺/⍵}⍳)3 ⍝ → (⍳3){⍺/⍵}(⍳3) → (⍳3)/(⍳3)
1 2 2 3 3 3
(⍳(/∘⊢)⍳)3 ⍝ → (⍳3)/⊢(⍳3)
1 2 2 3 3 3
(2/⍳)3 ⍝ Agh-fork is OK
1 1 2 2 3 3