Current issue

Vol.26 No.4

Vol.26 No.4

Volumes

© 1984-2024
British APL Association
All rights reserved.

Archive articles posted online on request: ask the archivist.

archive/16/3

Volume 16, No.3

This article might contain pre-Unicode character-mapped APL code.
See here for details.

Note that this article contains quite a bit of APL code - this is in APL2741 font, downloadable from this site (32K). There is a new problem in showing the {match} symbol in IE5 (it is treated as an optional hyphen), so it has now been duplicated in the font at position ch(166) which will paste into APL*PLUS as the split-stile character.

SHARP APL Operators

by Dan Baronet (danb@dsuper.net)

Introduction

Following Soliton’s announcement to unleash their APL into the Linux community I decided to review the differences between SHARP APL® and APL2 style APLs (IBM’s APL2, Dyalog, APL+Win). I have divided the report into several articles, each with a different topic.

This article is the second of the series. In the first one I showed the differences between the languages and enclosed arrays handling. This one is specific to the operators in SHARP APL.

Not having a current version of IBM’s APL2 and APL+Win on hand I have been making my observations based on Dyalog APL using Œml=2.

When reading this text keep in mind the date it was written (September 1999) as languages change and may render this information incorrect in the future.

Conventions

In the following text SAPL refers to SHARP APL, including SAM which is SAPL under MVS and SAX which is SAPL under Unix. SAX is the one also running under Linux and compared to APL2 here.

Variables and workspace names are enclosed in ‘quotes’. Functions and files are enclosed in <angle brackets>.

Italicized words usually precede their definition.

The term atom refers to a numeric or character scalar only. The term item refers to any scalar.

Whenever possible I will show examples in SAX and APL2 side by side.

To better understand this article you should first read the preceding article titled “SHARP APL vs. APL2 coding style” (see Vector 16:2 p.87).

Because I will use many examples involving monadic and dyadic functions I will use <mx> to denote a function used in a monadic way (where ‘x’ may be a number as in <m1>, <m2>, etc.) and <dx> to denote a dyadic function as in m3 x d2 y.

I use the terms ‘list’ and ‘vector’ in the same way. Same with the terms ‘table’ and ‘matrix’.

A Bit of History

All of the composition operators described here were introduced in the late 70s. Unfortunately they were never used to their full potential because of implementation restrictions in SAM. With the arrival of SAX those barriers were removed.

An important distinction between SAPL and APL2 is that is it not possible to create your own operators in SAPL.

Types of Operators

There are several kinds of operators. They all use a mixture of data and functions as arguments.

The main category discussed here covers the composition operators using functions only as arguments. Before I discuss these, I will introduce another operator which I will use in examples.

With with data arguments

There is a way to glue constants to a dyadic function to change it into a monadic function. For example, consider the function “catenate 1 2 3 before each item”:

SAX                                    APL2

1 2 3¨,¨>x            1 2 3°,¨x

Here ¨ is first used with a numeric (data=1 2 3) left argument to create such a function. Same with ° for Dyalog. An equivalent statement would have been

(<1 2 3),¨>x          (›1 2 3),¨x

It is also possible to use the data to the right, as in “divide by 3 and 5”:

÷¨3 5¨>x              ÷°3 5¨x

for which an equivalent statement could be

(<3 5)÷›¨>x           (›3 5)÷þ¨x

or, without swap (the SAX operator),

x÷¨><3 5              x÷¨›3 5

When a data argument is used with ¨ (or °) the entire data list is used. Notice what happens next:

4 = ½÷¨3 6¨>¼4        4=½÷°3 6¨¼4

SAX applied the function “divide by the vector 3 6” and returned 4 results (of 2 elements each). Dyalog applied the same function to each one of ¼4 yielding 4 enclosures of 2 results each also. If we remove the ¨> in SAX the 4 results are reassembled into a 4 by 2 table (matrix):

4 2 ¦ ½ ÷¨3 6 ¼4

Notice how the ‘3 6’ is part of the ÷¨ and the interpreter does not attempt to do 3 6¼4 instead. SAX also “knows” that divide is done on scalars whereas in Dyalog the ¨ has to be specified in order for the function to be applied on each item.

Swap, () monadic use

Swap, is used to swap arguments to a function. Its monadic use implies the argument is the same to the left. For example, if bƒb returns the number of contiguous 1s to the right of boolean ‘b’ then so does ƒ›b. To remove trailing spaces in a series of enclosed words we can try

(-ƒ›ð>' '=¨>w)‡¨>w

Composition operators

There are 3 composition operators in SAPL: with1 (¨), on (ð) and upon (ÿ). Their syntax is always x f¨g y and they are ambivalent. As described in the preceding article, with differs from on only in applying the inverse of g after f and g and requires knowing the inverse of g. Ex:

½¨>x                   ½¨x

Here, for each cell, we first disclose the cell, apply shape then apply the inverse of disclose, namely enclose, to the result. In APL2 the disclose/enclose is implicit and never specified. Nor can any other function be used with the ¨ operator.

To get the shapes of all elements together in a simple array in the preceding example one must either disclose the final result:

>½¨>x          œ½¨x

or, in SAPL, use on instead:

½ð>x

Those 2 operators (with and on) assume g is monadic and f is ambivalent. Like in APL2 where f is ambivalent.

The individual results are assembled the same way, whether they are disclosed together using >f¨> or as a result of doing fð>.

The ability to specify a function different from disclose in SAPL leads to interesting cases. For ex2:

®¨³x           (¯2†1 1,½x)½x

Another is

Ÿ\¨²b          ²Ÿ\²b

to scan backwards.

Upon (ÿ)

This operator differs from on in the sense that this time g is ambivalent and f is monadic. On monadic calls, it doesn’t matter whether you use on or upon. That is ½ÿ>y and ½ð>y are the same.

What really is interesting is when you have a sequence of them. For example, assume we want to apply three functions in sequence to each cell of an array.

We can do

m1¨>m2¨>m3¨>y m1¨m2¨m3¨y
or we can do (Dyalog only down here)
m1ÿm2ÿm3¨>y m1°m2°m3¨y

The end result will likely be different. In the first case <m3> is called ½y times, then <m2> is called ½y times then <m1> is called ½y times. In the second case the sequence <m3,m2,m1> is called ½y times.

In Dyalog, ° is used to glue functions together, having the same effect in this monadic case only. I will often refer to Dyalog for examples in the following text as IBM’s APL and APL+Win do not have this feature.

Chaining functions

Chaining functions using the operators seen so far is not that obvious at first. We have to remember the meaning of ¨, ð and ÿ and keep in mind they have, like any other operator, full left scope unless modified by pairs of parentheses.

The monadic case is the same for both SAX and Dyalog. Each function is called in sequence.

In the dyadic case chaining functions is interpreted quite differently.

first Dyalog

In Dyalog all but the leftmost function are assumed to be monadic. The monadic functions are called sequentially until the last one which MAY be called dyadically. For example

x d1°m1°m2°m3¨y

is the same as doing, for each corresponding cell

x[i] d1 m1 m2 m3 y[i]

As seen earlier, using constants with dyadic functions makes them monadic and is allowed. For example:

x d1°m1°(2 3°d2)°(d3°'ds')°m2¨y

then SAX

In SAX the monadic functions are applied to both corresponding cells until the dyadic function is reached. The use of on and upon or parentheses around the functions may achieve the same or different result. Ex:

x  d1ðm1ðm2ðm3¨> y
x  d1ð(m1ÿm2ÿm3)¨> y

is the same and equivalent to

<(m1 m2 m3 >x[j]) d1 (m1 m2 m3 >y[j])

for each corresponding cell.

upon (ÿ) should be put just BEFORE the (only) dyadic function if it’s not the leftmost function, as in

x  m1ÿm2ÿd1ðm3ðm4¨>y

which is, for each corresponding cell,

<m1 m2 (m3 m4 >x[j]) d1 (m3 m4 >y[j])

Of course clarity may suffer a bit. By the way, this is all in accord with what J does.3

Interpretation of this last example

Remember operators bind “naturally” to the left. Reading from right to left we see that

  • ¨ tells us to apply the entire sequence to its left to each corresponding disclosed element of ‘x’ and ‘y’
  • the rightmost ð tells us to apply <m4> monadically to >x[j] AND >y[j] and pass these two results as arguments to dyadic function <m1ÿ...m3> whose
  • rightmost ð tells us to apply <m3> monadically to each argument and pass these two results as arguments to dyadic function <m1ÿ...d1> whose
  • rightmost ÿ tells us to use <d1> dyadically and pass its result as argument to monadic function <m1ÿm2> whose
  • ÿ tells us to use monadic <m2>4 and pass its result as argument to monadic function <m1>
  • which returns its result to ¨
  • which returns the result of the entire sequence after enclosing it

Personally I’d recommend breaking down the sequence if possible. Or write another function that does the same and call only that function, as in x myfn¨>y.

Dyalog’s dynamic functions can achieve the same result by doing

x {m1 m2 (m3 m4 ¸) d1 m3 m4 ¾}¨y

Examples

1: to perform integer division (floor of divide) of two lists of numbers we can code

x ˜ÿ÷ y            ˜x÷y

The difference here, is that the former is done cell by cell whereas the latter is done function by function (it cannot be done cell by cell in APL2).

2: let’s put a few features together. In this example we want to take the first “n” rows of each table in a list and then transpose them:

n ³ÿ†¨>m            ³¨n†[1]¨m

Here we have upon with short take with disclose. ‘n’ being a series of scalars (vector of integers).

3: find the rank of each cell:

½ÿ½¨>x              ½°½¨x

This is more likely to be the kind of code you will encounter.

Scalar functions

In APL2, the use of the each operator is assumed for scalar functions (like +). There is no need to specify it5. Ex:

x+¨>y               x+y
+¨>/x               +/x
+/¨>x               +/¨x

Rank

Often functions apply to cells of rank 0 (scalar functions like + or -) or of rank infinite (like <). I only know of one other function, matrix divide, which is of a different rank, namely 2.

Normally, matrix divide applies to matrices. If you try to use it on a 3d array APL2 complains (RANK error). In SAX it won’t. Try

4 3 5 ¦ Ž4 5 3½i°.+i„¼4

Each 5 by 3 matrix (4 of them) was inverted to give four 3 by 5 matrices. Ž is said to be of rank 2. Each “cell” to which Ž was applied was of rank 2. All were 5 by 3 matrices. There were four of them. A 4-element vector of 5 by 3 matrices.

In SAPL this concept of “frame” (the vector in this case) and “cell” (the matrices) is of utmost importance. It breaks up data into piecemeal sections to be fed to functions.

We can view this 4 by 5 by 3 array as a scalar (the frame) containing a 4 by 5 by 3 matrix (the cell) OR as a four-element vector (the frame) of 5 by 3 matrices (the cells) OR as a 4 by 5 matrix of three-element vectors OR as a 4 by 5 by 3 matrix of scalars.

Most functions have a default rank. The function rank determines the rank of the argument cells. For monadic Ž it is 2, a matrix. For + is 0, a scalar. For < it is infinite. Any structure will do.

For dyadic functions two ranks exist: the left rank and the right rank. Function ¹ has two ranks (it doesn’t have a monadic rank): a left hand rank of 0 (for each item) and a right hand rank of infinite (membership is checked against all of it).

SAX allows you to modify the rank of primitives and to specify the rank of YOUR functions when you use them.

More examples

We’ve all been facing this problem: we need to add a vector of numbers, either to each line of a as in

m+(½m)½v (example 4)

or add a different scalar to each line of ‘m’ as in

m+³(²½m)½v (example 5)

These are fine but there are sometimes situations that require more thinking than you’d like to do. If we ponder this we see that in the expression ‘m+v’ the add function is trying to add numbers 1 by 1 (scalar by scalar) and is running out of numbers on one side resulting in a length error of some sort.

We can change that in SAPL. We can re-specify its behaviour by saying: “break down your work into a vector (frame of shape 5) of vectors (cell of shape 3)” (assuming ‘m’ is a 5 by 3 and ‘v’ is a 3 number list).

For example 4 this becomes:

m +ð1 v m+[2]v (as in APL+Win)

We modified + to do 5 additions of vectors of 3 elements each. We end up with 5 results of 3 elements each, a 5 by 3 matrix result.

Here the ð with a numeric right argument is called the rank operator and modifies its left argument (here +). The 1 to its right specified how many trailing axes (1 here, vectors) of the arguments were to be part of the cell sections. This made a left hand frame shape of (¯1‡½m) cells, or 5, and a left hand cell shape of (¯1†½m), or 3. The same ‘ð1’ also made a right hand frame shape of (¯1‡½v), or ¼0 (a scalar), and a right hand cell shape of (¯1†½v) or 3. Cell sizes are equal (3) and frame shapes are acceptable (5 elements + scalar). All is well.

In example 4 we divided the 5 by 3 matrix into 5 vectors of three-element vectors adding them to [a scalar containing] a three-element vector resulting in 5 results of three-elements each.

In example 5 we needed to add each vector in ‘m’ to each scalar in ‘v’. We needed to modify <+> to take vectors on one side and scalars on the other:

m + ð1 0 v m+[1]v (APL+Win)

Here we are saying: “add each vector in ‘m’ (the 1 on the same side as ‘m’) to each scalar in ‘v’ (the 0 on its side)”

There are two numbers to the right of the rank op to specify the rank of each argument. When they are the same, or if the function is monadic, one number may be used as in the first example.

Up to three numbers may be specified to denote monadic, left and right dyadic ranks but since these cannot be stored with a user’s function definition it’s unlikely you’ll ever see code with all three.

6: ravel each plane of a 3d array

,ð(2)7 3 4 ½ 5 6           ,[2 3]7 3 4½5 6

The parentheses are used to separated the 2 from the ‘7 3 4’ here. Or you could surround the right argument with parentheses:

7 12 ¦½,ð2 (7 3 4 ½ 5 6)

or you could use the pass function (or any function that does not alter the argument):

,ð2¤7 3 4 ½ 5 6

otherwise ‘2 7 3 4’ would be the argument to ð and ,ð2 7 3 4 would be applied to ½5 6 instead. Not the same thing.6

APL2 allows ravel to be applied to other consecutive dimensions as well. SAPL does not.

³,ð2¤2 3 1³7 3 4½x          ,[1 2]7 3 4½x

or

,ð2¨³2 1 3³7 3 4½x

7: enclose each line of a table

<ð1 table

Going back to example 4, we could re-code it as

(<ð1 m)+ð><v                œ(›[2]m)+›v

and example 5 as

(<ð1 m)+ð>v                 œ(›[2]m)+v

If the frame size needs to be specified (as opposed to the cell size) simply use negative (complementary) rank. For example, monadic function table (®) is defined as

,ð¯1¤1/x

meaning “ravel all but the first axis of the argument”, making sure ‘x’ is at least a vector (1/x). For a 3d array all planes (rank 2) are raveled, for a matrix all vectors (rank 1) are raveled and for vectors all scalars (rank 0) are raveled.

8: create an array from the shape of another.

Here we wish to create a table of ‘abc’s with the frame of another:

'abc'�ð1 x                  ((¯1‡½x),3)½'abc'

9: apply rank to your own functions.

Here we have a function, <R2fn> which takes a matrix (and nothing else) as argument and returns ten numbers. To use it on higher rank arrays we tell APL to use matrices as cells (x„2 3 4 5½¼99):

2 3 10¦½R2fnð2 x            œR2fn¨›[¯2†¼½½x]x

Cut (nð)

This is a different function. It uses ð again but this time with a left numeric argument.

This is the same as Dyalog’s dyadic with variants. APL+Win uses Œpenclose instead. Both of these work along the last axis unless brackets are used. SAX works along the first axis.

In Dyalog, bx, with ‘b’ boolean, cuts pieces of ‘x’, using 1s in ‘b’ as delimiter to indicate the start of a partition, and encloses them.

In SAX, b nð<x does the same with n=1. ‘b’ specifies where to cut and n=1 means the start cuts where b=1. Variants exists and n=2 means that each cut ENDS on a 1. Ex: b is 0 1 0 1 0 0 1 0, Œio is 1

     b 1ð<¼8                b›¼8
(2 3œ4 5 6œ7 8)             („answer)
     b 2ð<¼8                (¯1‡(Ÿ/b),b)›¼8
(1 2œ3 4œ5 6 7)

The monadic case assumes the first cell is a delimiter. For ex, with s„',az,bc,def':

     1ð<s                   (s¹1†s)›s
(',az'œ',bc'œ',def')        („answer)

To exclude the delimiter use a negative left argument:

     ¯1ð<s                  1‡¨(s¹1†s)›s
('az'œ'bc'œ'def')           („answer)

Function <vtom> revisited:

This function has to be the function most rewritten in APL. The < used so far can be changed to any monadic function. If we use pass we get

     ¯1ð¤s                  œ1‡¨(s¹1†s)›s
az
bc
def

As usual the pieces are reassembled to fit the largest.

Cut also works on higher rank arrays but always on the first dimension. Ex. b1 0 1 0 0 and m5 3½¼21:

b 1ð(+š)m                   œ+š¨b›[Œio]m

To cut on another axis use rank (here the last one):

b 1ð(+/)ð1 m                œ+/¨b›m

More examples

10: suppose we are given a list of series (a vector of vectors) of resistors and wish to find the overall resistance of each series (formula ÷+/÷) we could use

÷ÿ(+/)ÿ(÷ð1)ÿ>list          ÷°(+/)°÷¨list

We use ÷ð1 because ÷ is a scalar (rank 0) function and composition operators propagate the rank of the first function. Had we not done so +/ would also apply to each scalar and we would not get the result expected.

Because the inverse propriety of ÷ð1 is not defined we cannot do

+/¨(÷ð1)ð>list

11: compare two matrices of the same width, line by line. This is the same as v1°.=v2 for matrices:

m1°.(¦ð1) m2                (›[Œio]m1)°.¦›[Œio]m2

12: this is the same problem as 2 except that ‘m’ is of various higher ranks arrays. Were it not for the rank operator the solution would be harder to come by.

n ³ÿ(†ð2)¨>m (solution too complex in APL2)

Dot (.)

This operator behaves as in other APLs except that it can also be used monadically. Its definition is fairly complex for the general case but it should be enough to know that -.× returns the determinant of a matrix. Ex:

100 = -.× 3 3 ½ 3 4 7 6

Select (})

This operator takes a selection function, like 1 0/ or 2 3{, applicable to the right argument and merges the left argument within it. In APL2 this merging occurs within a variable. In SAX a result is returned instead:

x„9 (2¨{) }¼3               x„¼3 ª (3œx)„9

Here, 2¨{ selects the 3rd element from ¼3 which is replaced by 9.

We can do specific items or whole lines. Here the selector function is “b/”:

x„8 b/}2 3½¼6               x„2 3½¼6ª(b/x)„8

Conclusion

SAX is definitely different from APL2 when it comes to using operators. Sometimes it is easier to use, sometimes APL2 is easier. J programmers will be at (better) ease with this material as most operators are already defined this way in J.

I understand both worlds. I wish we could define our own operators though. Later maybe.

For questions and remarks I can be reached at danb@dsuper.net.

Dan Baronet
Montreal
Canada.

  1. in SAX the term used is under but I will stick with with here.
  2. only valid for rank 2 or less objects.
  3. in J, ¨ is &., ð is & and ÿ is for function arguments.
  4. it doesn’t matter which operator (on or upon) is used from then on since functions can only be called monadically.
  5. this is consistent with the definition that all scalar functions apply disclose BEFORE the operation and enclose AFTER, with the fact that scalars CANNOT be enclosed.
  6. actually it wouldn’t work as ð would issue a length error.

script began 0:43:09
caching off
debug mode off
cache time 3600 sec
indmtime not found in cache
cached index is fresh
recompiling index.xml
index compiled in 0.193 secs
read index
read issues/index.xml
identified 26 volumes, 101 issues
array (
  'id' => '10000770',
)
regenerated static HTML
article source is 'HTML'
source file encoding is ''
read as 'Windows-1252'
URL: ../apl2741.zip => trad/v163/../apl2741.zip
URL: mailto:danb@dsuper.net => mailto:danb@dsuper.net
URL: #fn1 => art10000770#fn1
URL: #fn2 => art10000770#fn2
URL: #fn3 => art10000770#fn3
URL: #fn4 => art10000770#fn4
URL: #fn5 => art10000770#fn5
URL: #fn6 => art10000770#fn6
URL: mailto:danb@dsuper.net => mailto:danb@dsuper.net
completed in 0.224 secs