我试图学习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
}
感谢您的帮助!
答案 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运算符(::)将元素追加到列表的前面。因此,必须颠倒最终名单。