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 theSrefer to the left argument ofSor 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.