Haskell简单的初学者排序程序

时间:2018-07-04 00:00:38

标签: sorting haskell

对于Haskell还是很新的。我正在尝试使用递归编写排序函数,而这是我到目前为止所拥有的:

sort' :: [Int] -> [Int]
sort' [] = []
sort' [x] = [x]
sort' (x:y:xs) = 
  if x > y
    then y:sort' (x:xs)
    else x:sort' (y:xs)

根据我的判断,当要排序的元素少于2个时,它可以正常工作。但是,如果我输入[3,2,1],则会得到[2,1,3]。我尝试手动按照输入的路径进行操作,但无法弄清楚如何对两个以上的初始元素进行排序。我已经读了一些书,并且正在考虑涉及iterate并使用列表长度(length)的问题,但是我不确定如果它可以工作怎么实现。

2 个答案:

答案 0 :(得分:2)

您的sort'实现了冒泡排序的迭代,因此,一遍又一遍地应用它会得到一个完全排序的列表。有many ways of doing that,其中一个是使用iterate重复应用,然后使用!!选择第N个应用:

completeSort list = iterate sort' list !! length list

答案 1 :(得分:0)

正如其他人指出的那样,您可以使用iteratesort'应用于列表n次,这是在Haskell中使用高级组合器的绝妙示例(与原始递归相反)。另一个有用的组合器是untiluntil将使用谓词:: a -> Bool,函数:: a -> a和值:: a,并将该函数应用于该值,直到谓词保持为真。因此,当您遇到类似情况但又不知道要在输入中进行多少次传递时,可以执行类似until isSorted sort' xs的操作,

Haskell中还有许多其他的一遍排序算法也很不错,包括自上而下和(特别是自下而上)合并排序。

冒着把您带入理论深处的风险,我想提一下另一种相当不错的高层组合方式,用于考虑n次的迭代。这基于“递归方案”的思想,其中的基本思想是递归数据类型的结构告知有用的模式以进行处理/转换。碰巧的是,Haskell足够强大,可以表达很多这样的想法。递归方案最简单的例子之一是基于自然数:

data Nat = Zero | Succ Nat

这是一种递归数据类型,因为第二个数据构造函数本身返回了Nat的引用。有一种非常自然的处理Nat的方式,也就是说:当我拥有Zero时,我应该返回一些值;否则,我应该递归处理Nat,然后使用这些值得出一个新值。碰巧的是,我们可以用另一种方式写Nat,这使得通常很容易做到这一点:

data NatF a = Zero | Succ a
type Nat = Mu NatF

现在,NatF数据类型可以在其“递归”组件中保存任何类型的值,而不仅仅是另一个Nat。第二行中的Mu类型构造函数基本上类似于fix函数,但是对于类型:它实例化NatF (NatF (NatF (NatF ...)))以便Nat = NatF Nat,这与我们之前的定义相同。

但是,由于通用性的提高,我们现在可以写下一种类型,该类型描述如上所述处理Nat的函数:NatF a -> a。当给定Zero时,此类型的函数仅返回一些a;给定Succ时,它可以访问在“内部” a上递归调用时返回的Nat。事实证明,可以概括执行此操作所需的基础架构,以便它可以在以上述样式编写的任何类型上工作(即作为在递归组件上概括的类型的固定点)。这意味着您可以根据递归调用的结果为函数编写方程式,然后在此数据结构上调用cata,而不必费心写下重复的无聊递归调用!

这还意味着将您的“应用排序”(长度xs)时间写入xs的另一种方法是:

sort xs = cata phi (toNat $ length xs)
  where phi Zero = xs
        phi (Succ xs) = sort' xs