【发布时间】:2013-10-01 12:57:07
【问题描述】:
我有一个函数,它接受一个 int 和一个 int 列表。它返回一个小于第一个参数的整数列表。
我想在不使用 List.Filter 的情况下复制相同的代码:
let less e L =
L |> List.filter(fun a -> a < e)
less 3 [1;2;4;5]
我想学习递归思考,就好像我在用 SML 写这篇文章一样
我将如何递归地编写这个?
【问题讨论】:
我有一个函数,它接受一个 int 和一个 int 列表。它返回一个小于第一个参数的整数列表。
我想在不使用 List.Filter 的情况下复制相同的代码:
let less e L =
L |> List.filter(fun a -> a < e)
less 3 [1;2;4;5]
我想学习递归思考,就好像我在用 SML 写这篇文章一样
我将如何递归地编写这个?
【问题讨论】:
基本上逻辑是这样的。对于基本情况,如果列表为空,那么您就完成了。只需返回空列表。对于头部有项目 (x) 和尾部 (xs) 的列表,如果 x < e 返回 x 将 less 的结果附加到尾部。否则,只返回应用于尾部的less。
let rec less e L =
match L with
| [] -> []
| x :: xs -> if x < e then x :: (less e xs) else (less e xs)
但是,这有点低效,因为它不是尾递归的。尾递归调用是立即返回递归调用的结果。这些更有效,因为编译器可以将它们基本上转换为循环,每次递归不会消耗额外的堆栈空间。为此,我们需要一个带有累加器的辅助函数:
let lessTailRec e L =
let rec loop e L acc =
match L with
| [] -> acc
| x :: xs -> if x < e then loop e xs (x :: acc) else loop e xs acc
loop e L [] |> List.rev
【讨论】:
我不确定这是否可以编译,因为我只是在 SO 中编写了它。此外,这当然不是最好的方法,但它可以让你走上递归轨道(mike z's 展示了一种更好的方法)。
let rec less e L =
match L with
| [] -> []
| (head::tail) -> if head < e then (head :: less e tail) else (less e tail)
基本上,它由几个步骤组成。
首先将头部从列表中拉出(如果是空列表,则返回一个空列表)
第二个是比较head和e
第三个是将列表的尾部发送到递归行
第四是重建列表,可选地包括列表的头部。
List.Filter 函数只是一个通用函数,它允许您注入一个函数来替换第二步,并为您节省所有样板文件。通常您希望在所有情况下都使用 List.Filter,除非在学习它的作用时。
【讨论】: