我试图理解APL中的经典快速排序算法:
Q←{1≥≢⍵:⍵ ⋄ S←{⍺⌿⍨⍺ ⍺⍺ ⍵} ⋄ ⍵((∇<S)⍪=S⍪(∇>S))⍵⌷⍨?≢⍵}
有一些我不理解的事情,还有一些我不喜欢的风格选择,所以我要列出所有这些问题。希望有人能够解释一下。
- 我知道在
{ }
defn 内部,⍺
是左参数,⍵
是右参数。在S←{⍺⌿⍨⍺ ⍺⍺ ⍵}
中,⍺⍺
是什么?同样地,是否存在一个⍵⍵
?S
中的⍺
是指 S 的左参数 还是 Q 的左参数?
我猜测 S
中的 ⍺
指的是 S
的左参数。而 ⍺⍺
指的是 封闭函数(即 Q 的 ⍺
)的 ⍺
。
⍨
)?代码不是更清晰,写成这样吗:Q←{1≥≢⍵:⍵ ⋄ S←{(⍺ ⍺⍺ ⍵)⌿⍺} ⋄ ⍵((∇<S)⍪=S⍪(∇>S))⍵[?≢⍵]}
我能想到使用 commute 的唯一用途是减少使用括号
()
和 []
,但这似乎不值得为了简洁而损失可读性。我是否在 "APL way" 上漏掉了什么?
这实际上并没有执行快速排序,是吗?快速排序被定义为原地排序。然而,我对APL语义的理解是,这段代码实际上在递归子调用中构建新数组,然后使用
⍪
将它们连接起来。事实上,这也是针对Haskell快速排序提出的批评。在APL语义中,有什么东西告诉我们这个操作是“原地”完成的吗?请注意,我对“足够聪明的编译器”论点不感兴趣,因为数组分析基本上是具有挑战性的。如果APL编译器确实将其转换为原地算法,我将非常珍视关于它如何进行此分析的详细信息——这是相当了不起的成就!为什么要使用
≢⍵
来查找维度大小?为什么不使用⍴⍵
?通常,我发现人们在查询沿最外层维度的大小时会使用≢
而不是⍴
,即使该函数仅在一维情况下有效。再次说明,我认为我可能没有理解APL的方式。
非常感谢。