【发布时间】:2020-06-18 20:42:42
【问题描述】:
我正在尝试理解 APL 中的经典快速排序:
Q←{1≥≢⍵:⍵ ⋄ S←{⍺⌿⍨⍺ ⍺⍺ ⍵} ⋄ ⍵((∇<S)⍪=S⍪(∇>S))⍵⌷⍨?≢⍵}
有些东西我不明白,有些风格选择让我很困扰,所以我将把它们全部列出来。我希望有人能给我解释一下。
- 我知道在
{ }defn 中,⍺是左参数,⍵是右参数。S←{⍺⌿⍨⍺ ⍺⍺ ⍵}中的⍺⍺是什么?同样,是否有⍵⍵?S中的⍺是指S的left 参数 还是Q的left 参数?
我的猜测是S 中的⍺ 指的是S 的左参数。 ⍺⍺ 指的是封闭函数的⍺(即Q的⍺)。
- 为什么要大量使用通勤 (
⍨)?代码是不是更清楚地写成:
Q←{1≥≢⍵:⍵ ⋄ S←{(⍺ ⍺⍺ ⍵)⌿⍺} ⋄ ⍵((∇<S)⍪=S⍪(∇>S))⍵[?≢⍵]}
我能想到的使用通勤的唯一用途是减少括号 () 和 [] 的使用,但这似乎不值得失去易读性。我在这里错过了一些“APL方式”吗?
这实际上并不是在执行快速排序,是吗?快速排序被定义为就地。但是,我对 APL 语义的理解是,这段代码实际上在递归子调用上构建了新数组,并使用
⍪将它们连接起来。确实,这是same criticism that is levelled at Haskell's quicksort。我在 APL 语义中是否遗漏了一些东西来告知此操作是“就地”完成的?请注意,我对“足够聪明的编译器”参数不感兴趣,因为数组分析从根本上具有挑战性。如果 APL 编译器确实将其转化为就地算法,我会非常重视有关它如何执行此分析的详细信息 --- 这真是一项成就!为什么要使用
≢⍵来查找维度大小?为什么不⍴⍵?一般来说,我发现人们使用≢而非⍴来查询沿最外层维度的大小,即使函数工作的唯一情况是在一维中。同样,我认为我在 APL 方式中缺少某些东西。
非常感谢。
【问题讨论】:
标签: quicksort in-place apl dyalog