使用fold_left / right反转OCaml中的列表

时间:2011-09-11 23:56:48

标签: recursion functional-programming ocaml higher-order-functions fold

更新 - 解决方案

感谢jacobm的帮助,我想出了一个解决方案。

// Folding Recursion
let reverse_list_3 theList = 
    List.fold_left (fun element recursive_call -> recursive_call::element) [] theList;;

我正在学习OCaml中的不同递归方式(对于类)和一些练习,我正在编写一个函数来使用不同的递归样式来反转列表。

// Forward Recursion
let rec reverse_list_forward theList =
    match theList with [] -> [] | (head::tail) -> (reverse_list_1 tail) @ [head];;

// Tail Recursion
let rec reverse_list_tail theList result =
    match theList with [] -> result | (head::tail) -> reverse_list_2 tail (head::result);;

现在,我正在尝试使用List.fold_left编写反向函数,但我陷入了困境,无法弄明白。如何使用折叠编写这个反向函数?

另外,如果有人对函数式编程有很好的引用,不同类型的递归,高阶函数等等,链接将不胜感激:)

2 个答案:

答案 0 :(得分:8)

我觉得将折叠操作看作是对操作序列做什么的概括很有帮助

a + b + c + d + e

fold_right (+) 0使用+作为基本情况,以关联方式应用0操作:

(a + (b + (c + (d + (e + 0)))))

fold_left 0 (+)以左关联方式应用它:

(((((0 + a) + b) + c) + d) + e)

现在考虑一下,如果您将+替换为::,将0替换为[],则会在右侧和左侧折叠中发生。


考虑fold_leftfold_right作为“替换”列表中的::[]运算符的方式也可能很有用。例如,列表[1,2,3,4,5]实际上只是1::(2::(3::(4::(5::[]))))的简写。将fold_right op base视为让::替换op[]替换为base可能会有用:例如

fold_right (+) 0 1::(2::(3::(4::(5::[]))))

变为

1 + (2 + (3 + (4 + (5 + 0))))

::成为+[]成为0。从这个角度来看,很容易看到fold_right (::) []只是让你回到原来的列表。 fold_left base op做了一些奇怪的事情:它重写了列表周围的所有括号以转向另一个方向,将[]从列表后面移到前面,然后然后::替换为op,将[]替换为base。例如:

fold_left 0 (+) 1::(2::(3::(4::(5::[]))))

变为

(((((0 + 1) + 2) + 3) + 4) + 5)

使用+0fold_leftfold_right会产生相同的结果。但在其他情况下,情况并非如此:例如,如果您使用+代替-,结果会有所不同:1 - (2 - (3 - (4 - (5 - 0))) )= 3,但是(((((0 - 1) - 2) - 3) - 4) - 5)= - 15.

答案 1 :(得分:0)

let rev =
  List.fold_left ( fun lrev b ->
    b::lrev
  ) [];;

测试:

# rev [1;2;3;4];;
- : int list = [4; 3; 2; 1]