I am attempting to understand the classic quicksort in APL:
Q←{1≥≢⍵:⍵ ⋄ S←{⍺⌿⍨⍺ ⍺⍺ ⍵} ⋄ ⍵((∇<S)⍪=S⍪(∇>S))⍵⌷⍨?≢⍵}
There are some things I don't understand, and some stylistic choices that bother me, so I'm going to list all of them out. I hope someone can explain them to me.
- I understand that inside a
{ }
defn,⍺
is the left argument, and⍵
is the right argument. What is⍺⍺
inS←{⍺⌿⍨⍺ ⍺⍺ ⍵}
? Similarly, is there an⍵⍵
? Does the⍺
inside theS
refer to the left argument ofS
or the left argument ofQ
?
My guess is that ⍺
inside the S
refers to the left argument of S
. The ⍺⍺
refers to the ⍺
of the enclosing function (ie, the ⍺
of Q).
- Why the copious use of commute (
⍨
)? Is the code not far clearer written as:
Q←{1≥≢⍵:⍵ ⋄ S←{(⍺ ⍺⍺ ⍵)⌿⍺} ⋄ ⍵((∇<S)⍪=S⍪(∇>S))⍵[?≢⍵]}
The only use I can think of using commute is to reduce the use of brackets ()
and []
, but that hardly seems worth the loss in legibility. Am I missing something of the "APL way" here?
This is not actually performing quicksort, is it? Quicksort is defined as being in-place. However, my understanding of the APL semantics is that this piece of code in fact builds new arrays on the recursive sub-calls, and the concatenates them using
⍪
. Indeed, this is the same criticism that is levelled at Haskell's quicksort. Is there something I am missing in the APL semantics that informs that this operation is done "in-place"? Do note that I am not interested in "sufficiently smart compiler" arguments, since array analysis is fundamentally challenging. If the APL compiler does actually turn this into an in-place algorithm, I would greatly value details on how it manages to perform this analysis --- that's quite an accomplishment!Why the use of
≢⍵
to find the dimension size? Why not⍴⍵
? In general, I find that people use≢
over⍴
to query for the size along the outermost dimension, even when the only case where the function work is in 1D. Again, I presume there is something in the APL way that I am missing.
Thanks a lot.