了解List.fold_left并使用它实现插入排序

时间:2012-09-08 20:07:48

标签: algorithm scala functional-programming ocaml

我试图学习OCaml,而且我还在努力学习折叠功能。我做了一些研究,并且我发现了以下使用foldleft实现插入排序的代码片段(用Scala编写)。我想我明白了,但我对Scala一无所知,所以我还需要一点澄清。所以,我猜我的问题是2倍(ha,得到它?),这个函数将如何用Ocaml编写,它是如何实际工作的?

def insertionSort[A <% Ordered[A]](list: List[A]): List[A] =
  list.foldLeft(List[A]()) { (r,c) =>
    val (front, back) = r.span(_ < c)
    front ::: c :: back
  }

感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

正如我上面评论的那样,这不是一种特别好的排序方式(假设我理解Scala代码,我可能不会这样)。以下是OCaml对其似乎正在做的事情的翻译:

let insertion_sort l =
    let add1 sortedl x =
        let le, gt = List.partition ((>=) x) sortedl
        in
            le @ [x] @ gt
    in      
        List.fold_left add1 [] l

在您编写FP代码一段时间后,折叠变得非常自然。目的是以已知的顺序(例如,最左边的第一个)处理来自数据结构(例如,列表)的一系列元素,同时保持在处理时传递的一些状态。在最简单的情况下(如此处),状态也是您的最终答案。在其他情况下,您需要携带一些额外的状态以及最终答案。在这些情况下,您最后会提取最终答案。

也许我不应该这样说,但是折叠就像C系列中的for(;;)语句一样,除了它封装了你计划在循环中修改的所有状态。通过这种方式,它是一种控制得很好的迭代形式。

通信看起来像这样:

for(state = S, x = first(D); more(D); x = next(D)) { state = E(state, x) }

fold_left (fun state x -> E state x) S D

封装修改后的状态的规则通常会在您习惯之后变得非常有用。通常,折叠的函数(如上面的add1)在许多其他情况下都很有用,因为折叠的结构需要在该点中具有通常有用的功能。

请注意,特别是,fold函数包含有关如何遍历数据结构的所有知识。折叠功能只需知道如何处理每个元素。因此,您可以自由更改数据结构(和折叠),而无需更改任何其他代码。您还可以使用具有不同数据结构的相同折叠函数(add1)。

答案 1 :(得分:0)

let insertion_sort (l: int list) : int list = 
  let rec insert (e: int) (a: int list) =
    match a with
    | [] -> e::a 
    | h::t -> if (e > h) then e::a else h::(insert e t)
  in 
  List.rev (List.fold_left (fun a e -> insert e a) [] l)

List.fold_left接受函数,累加器和列表。 累加器应该初始化为空列表[],这样当List.fold_left收到一个空列表作为&#39; l&#39;参数,它将返回一个空列表,因为没有什么可以排序。

(有趣的是e - &gt;插入e a)更新累加器。当List.fold_left在列表中折叠时,它将获取每个元素并将其插入到已排序列表的正确位置。辅助函数让rec插入执行此作业。

let rec insert (e: int) (a: int list) =
  match a with
  | [] -> e::a 
  | h::t -> if (e > h) then e::a else h::(insert e t)

首先看到排序列表是否为空。如果是,则将元素插入空列表。否则(排序列表不为空),然后它将要插入的元素与排序列表的头部进行比较,并递归检查以查找排序列表中元素的排序位置。

最后,调用List.rev是因为insert函数使用cons运算符(::)将元素追加到列表的前面。因此,必须颠倒最终名单。