Skip to content

ALL ABOARD!

This part will cover

  • Trains
  • Defining 2- and 3-trains
  • The tack functions

Some other important functions are the dyadic right and left identity/"tack" functions which return their right or left arguments.

       'True''False'
True
       'True''False'
False
When applied monadically, they return their only argument.

Consider the following pairs of functions, implemented using dfns first, and trains second.

       range_average  {((/)-⌊/),(+/)÷≢}
       range_average 5.48 6 5.63 6.02 5.37
0.65 5.7
         range_average  (/-⌊/),+/÷≢
       range_average 5.48 6 5.63 6.02 5.37
0.65 5.7

       plusminus  {(-),,+}
       5.7 plusminus 0.65
5.05 5.7 6.35
       plusminus  -,⊣,+
       5.7 plusminus 0.2
5.5 5.7 5.9

Notice that the arguments of the function were not referred to in the function trains, this style of programming is called tacit or "point-free" programming, borrowed from mathematics where it means taking data that can be described using points to be more fundamental than the points themselves, avoiding the need to refer to points explicitly. In this case, taking functions to be more fundamental than their description in terms of explicit arguments, w

The most basic train is the 2-train (fg), in operator form f⍤g, called an atop. The atop evaluates the function f monadically on the result of g applied to the arguments of the train.

The following image is composed of three parts, the first being the APL syntax for the atop, the second being a tree-like representation of the atop where the evaluation happens from bottom to top, and the third is the atop in traditional mathematical notation.

Trains in RIDE

It is possible to make sense of trains by rendering them in various forms in the RIDE editor using the -trains option to the Box user command. For example, it is possible to render trains as trees using the following command.

]Box on -trains=tree
You can use the help ]? command to get help for any user command in RIDE.

Floored division can be conveniently expressed as an atop.

       12÷5
2.4
       2.4
2
       12(⌊÷)5
2
A very similar composition is achieved using the Beside operator, f∘g.

More simply, the expression ⍺(f∘g)⍵ evaluates to ⍺fg⍵, and recalling that APL is right associative, is ⍺f(g⍵).

       matrix  3 3  (3|⍳9)
       matrix
1 2 0
1 2 0
1 2 0
        matrix ⍝ Transpose
1 1 1
2 2 2
0 0 0
       ⍝ Beside of matrix multiplication (+.×) and transpose (⍉)
       matrix (+.×) matrix
5 5 5
5 5 5
5 5 5
       matrix (+.×) matrix
5 5 5
5 5 5
5 5 5

       1 2 (-/×) 3 1 ⍝ Cross product
¯5
       ⍝ A beside of an atop and a function
       1 2 (-/×) 3 1
¯5

Notice that in the last statement, the atop -/× and function were composed into an atop. Since the atop -/× is a functions in its own right, it can be used as part of larger trains.

The Over operator, f⍥g, applies f to the value of g applied to each of its arguments.

The decibel conversion from the start of this chapter can be easily written in this form.

       dB_to_ratio  {10*÷10}
       ratio_to_dB  {10×10}
       ⍝Over of an atop and a function
       60 (ratio_to_dB÷)dB_to_ratio 30
30
       ratio_to_dB ((dB_to_ratio 60) ÷ dB_to_ratio 30)
13.01029996

       reciprocal_sum  {÷(÷)}
       1 reciprocal_sum 2
0.6666666667
       reciprocal_sum  (÷+)÷
       1 reciprocal_sum 2
0.6666666667

       hypotenuse  {((*2)+*2)*0.5}
       3 hypotenuse 4
5
       hypotenuse  (*0.5+)(*2)
SYNTAX ERROR
      hypotenuse(*0.5+)(*2)
                      

When trying to adapt the hypotenuse function to point-free programming, a seemingly strange error appears complaining about syntax of our train. However, this error should not seem so bizzare after some thought, because the expression (* 0.5 +) is not a function, in fact, (* 0.5 +) evaluates to (1.648721271+) since the * function here is interpreted to act monadically on 0.5. In order to attach the value 0.5 to * and turn the dyadic * into a monadic {⍵*0.5}, the bind operator can be used.

       hypotenuse  (*0.5+)(*2)
       3 hypotenuse 4
5

Another form of the hypotenuse function is obtained by taking the magnitude of a complex number, with real and imaginary parts the arguments of the function.

           complex  +/(1 0J1)×
           complex 3 4
3J4
           hypotenuse  |complex
           hypotenuse 3 4
5
         hypotenuse  |(+/(1 0J1)×)
         hypotenuse 3 4
5
         hypotenuse  |+/(1 0J1)×
         hypotenuse 3 4
5

Another form of the hypotenuse function is obtained by taking the magnitude of a complex number, with real and imaginary parts the arguments of the function.

Let’s take a closer look at the trains at the beginning of this section, applied to vector values.

       range_average  (/-⌊/),+/÷≢
       plusminus  -,⊣,+

Starting with range_average, we start reading from right to left identifying forks and atops. We first identify the 3-train +/÷≢, which takes the sum +/ of the elements of a vector and divides ÷ by the length of the vector, {(+/⍵)÷(≢⍵)} as a function which takes an average.

       avg+/÷≢
       range_average  (/-⌊/),avg

Then, the 3-train (⌈/-⌊/) takes the maximum ⌈/ and subtracts - by the minimum ⌊/, {(⌈/⍵)-(⌊/⍵)}, which gives the range of a series of values.

       avg+/÷≢
       range/-⌊/
       range_average  range,avg

Then it’s clear in this form that the function range_average takes the range and average of a series of values and returns both values in the form of a vector.

The plus_minus function can be analysed similarly.

       plusminus  -,⊣,+
       plusminus  -,(⊣,+)
       plusminus  -,{,+}
       plusminus  {(-),(,+)}

More generally, for an n-train of functions (f g h k l m … w x y z),

  • (f g h k l m … t u v w x y z) is interpreted as (f g h k l m … t u v w (x y z)), which can be interpreted again as (f g h k l m … t u (v w (x y z))), and further recursively.
  • If the number of functions is odd, then the result is of the form of forks over forks
    • (f g (h k (l m … (t u (v w (x y z))))…)
  • If the number of functions is even, then the result is an atop over forks
    • (f (g h (k l (m … (t u (v w (x y z))))…)