APL中的快速排序说明

时间:2020-03-06 06:16:08

标签: quicksort in-place apl dyalog

我试图了解APL中的经典快速排序:

Q←{1≥≢⍵:⍵ ⋄ S←{⍺⌿⍨⍺ ⍺⍺ ⍵} ⋄ ⍵((∇<S)⍪=S⍪(∇>S))⍵⌷⍨?≢⍵}

有些事情我不理解,有些样式选择困扰我,所以我将列出所有这些。我希望有人可以向我解释。

  1. 我知道在{ }定义中,是左参数,而是右参数。 ⍺⍺中的S←{⍺⌿⍨⍺ ⍺⍺ ⍵}是什么?同样,是否有⍵⍵内的S是指S left参数还是Q left参数?

我的猜测是中的S是指S的左参数。 ⍺⍺指的是封闭函数(即Q的)。

  1. 为什么大量使用通勤()?代码是否更清楚地写为:
Q←{1≥≢⍵:⍵ ⋄ S←{(⍺ ⍺⍺ ⍵)⌿⍺} ⋄ ⍵((∇<S)⍪=S⍪(∇>S))⍵[?≢⍵]}

我能想到的通勤唯一用途是减少括号()[]的使用,但这似乎不值得丢掉易读性。我在这里缺少“ APL方式”吗?

  1. 这实际上不是在执行快速排序,对吗? Quicksort定义为就地 。但是,我对APL语义的理解是,实际上这段代码在递归子调用上构建了新数组,并使用将它们连接起来。确实,这是same criticism that is levelled at Haskell's quicksort。我在APL语义中缺少什么东西来告知此操作是“就地”完成的吗?请注意,我对“足够智能的编译器”参数不感兴趣,因为数组分析从根本上具有挑战性。如果APL编译器确实将其转化为就地算法,我将非常重视其如何执行此分析的详细信息---这是一个很大的成就!

  2. 为什么使用≢⍵来查找尺寸大小?为什么不⍴⍵?通常,我发现人们使用而不是来查询沿最外层尺寸的尺寸,即使函数只能在1D中使用,也是如此。再次,我认为我缺少APL方式的东西。

非常感谢。

1 个答案:

答案 0 :(得分:3)

  1. 我知道在{ }定义中,是左参数,而是右参数。 ⍺⍺中的S←{⍺⌿⍨⍺ ⍺⍺ ⍵}是什么?同样,是否有⍵⍵

S←{⍺⌿⍨⍺ ⍺⍺ ⍵}称为 dop 。与 dfn 是用户定义的函数类似, dop 是用户定义的运算符,其行为类似于¨或{{ 1}}。

其语义概述:

  • 如果dop仅提及(而不是⍺⍺),则它将成为一元运算符,您可以将其用作⍵⍵
  • 如果dop提到x (m S) y,它将成为二元运算符,您可以将其用作⍵⍵
  • 在两种情况下,x (m S n) y(将具有⍺⍺的值)和m(将具有⍵⍵的值)可以是数组或函数。
  • 您还可以决定在掺杂物的主体中不使用n,在这种情况下,您可以将其命名为(m S) y,而忽略左侧的参数
  • (m S n) y被称为 left操作数,而m被称为 right操作数。与左参数n)和右参数x)不同。

在您的示例中,y仅提及S,因此被称为⍺⍺。如果您像x (m S) y那样呼叫S,它将求和为1 2 3 >S 2,它将是一个3。

1 2 3⌿⍨1 2 3 > 2中的是指S的左自变量还是S的左自变量?

Q的正文中,由S字符组成的所有内容均指的自变量/操作数。 S的原始参数是不可见的(除非首先将它们分配给变量,在这种情况下,它们作为变量名可见)。

  1. 为什么大量使用通勤(Q)?

我认为这主要是一种风格选择。我还更喜欢编写带括号的代码,而不是在生产代码中使用它,除非当使用很容易被识别为APL习惯用法时。我确实写例如用代替3÷⍨whatever进行常数除。

  1. 这实际上不是执行快速排序,对吗?

您是正确的。正如您已经提到的,Quicksort旨在就地运行以真正成为 Quick (TM)。 APL可以进行内存预分配和数组共享以减少一些内存副本和分配,但是当创建三个子数组(元素小于/等于/大于数据透视表)并创建并删除时,至少有一些副本是不可避免的。稍后串联。

要注意的一件事是,与Haskell不同,APL确实具有就地分配,看起来像(whatever)÷3。如果要在APL中正确实现Quicksort,则必须像在C中那样编码(将索引传递给递归调用等)。

  1. 为什么使用x[i]←v来查找尺寸尺寸?为什么不≢⍵

⍴⍵被称为“ tally”,而被称为“ shape”。 总是返回标量值,而返回一个向量(如果给定向量参数,则长度为一个向量)。虽然标量和长度为一的矢量在大多数情况下的行为相同,但它们 却是不同的东西,例如是错误的。

我相信这是区分两者的好习惯,只要有可能,就优先选择标量而不是长度一的向量。一个值得注意的例外是当您需要显式封闭的数组(标量不能被封闭)时。