Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rank operator vs axis notation

Tags:

apl

dyalog

I had a sneak peek at APL2 on the mainframe a number of years ago and remember being shown solutions to the problem of adding a vector to a matrix.

Given a←4 4 ⍴ ⍳16 and ⎕io←1

The old way of adding a vector to the rows was something like

a+(⍴a)⍴10 20 30 40

resulting in

11 22 33 44
15 26 37 48
19 30 41 52
23 34 45 56

and adding a vector to columns of a matrix was

a+(4 1⍴10 20 30 40)[;1 1 1 1]

or, if you prefer,

a+4/4 1⍴10 20 30 40

resulting in

11 12 13 14
25 26 27 28
39 40 41 42
53 54 55 56

Luckily, I was able to call up the guy who showed me APL2 that day (he's retired but still answers his phone) and ask about this second solution, who remembered right away what I was talking about.

The new APL2 way was much more concise, succinct, and consistent, those examples would be solved by a+[2] 10 20 30 40 and a+[1] 10 20 30 40. Cool. And it worked in Dyalog.

Fast forward a decade or more and I see there is this new thing called The Rank Operator. The first example can be solved by a(+⍤1) 10 20 30 40 (I am still trying to come to grips on the usage of parentheses, I think I actually regenerated some brain cells once I thought I understood a bit of this)

Be that as it may, there is no straightforward (at least to me) analogue to the second example a+[1] 10 20 30 40 using the rank operator. I can't say I understand it at all, but it seems to me that the rank operator "re-casts" its left argument by collapsing its dimensions while leaving the contents intact. Too many years of C++ and Java have influenced my way of thinking about things.

Is there a simple solution to a+[1] 10 20 30 40 using the rank operator? The only think I found so far is ⍉(⍉a)(+⍤1) 10 20 30 40 which misses the point and defeats the whole purpose.

Why would the rank operator be preferable to the axis notation? Which is "better"? (a loaded term, to be sure) At first glance, the axis notation was very easy for me, a guy with a shoe size IQ, to grasp. I couldn't say the same for the rank operator.

like image 608
Apollo 42 Avatar asked Oct 27 '17 04:10

Apollo 42


1 Answers

Is there a simple solution to a+[1] 10 20 30 40 using the rank operator?

Yes: a(+⍤1 0)10 20 30 40 Try it online!
 For dyadic application, the rank operator actually needs a two-element right operand (but accepts a singleton which it extends to two elements). a(f⍤A B)b means that a's rank-A subarrays should be paired with b's rank-B subarrays. So in your case, a(+⍤1)10 20 30 40 (which really means a(+⍤1 1)10 20 30 40) says that the rank-1 arrays of a (the rows), should be added to the rank-1 array(s) of 10 20 30 40 (the entire vector). Here, a(+⍤1 0)10 20 30 40, says that the rank-1 arrays of a (the rows), should be added to the rank-0 arrays of 10 20 30 40 (the scalars).

Why would the rank operator be preferable to the axis notation?

The rank operator allows you full control of what to grab from each argument, while bracket axis notation only allows you to extend the lower-ranking argument. The rank operator is an integrated part of the language, and can be used with any function, even user-defined ones, while the bracket axis notation can only be used with dyadic scalar functions, reductions, and a small subset of mixed functions.

Which is "better"?

Since the rank operator follows normal APL syntax for operators and is universally applicable, it reduces the amount of rules to remember. Furthermore, the rank operator also allows specifying relative ranks, by using negative numbers. So while ⍤1 means apply to subarrays of rank 1, ⍤¯1 means apply to subarrays of one lesser rank than the entire argument. In my opinion, this is more than enough to safely consider the rank operator "better" than bracket axis.

I am still trying to come to grips on the usage of parenthesis

I personally dislike parentheses, so I get where you are coming from. Luckily, you can always reduce the amount of parentheses as much as you like. The only reason for the parenthesis in a(+⍤1)10 20 30 40 is to separate the array operand 1 from the array argument 10 20 30 40. Any other way of separating them is acceptable too, and I usually use the identity function for this: a+⍤1⊢10 20 30 40 Try it online!
 However, you can also curry right operands to dyadic operators, yielding monadic operators, which read very nicely: horizontally←⍤1 0 ⋄ vertically←⍤1 1 Try it online!

The full rank operator documentation is available online

like image 74
Adám Avatar answered Oct 27 '22 18:10

Adám