【问题标题】:How to create a new list in a recursive F# function?如何在递归 F# 函数中创建新列表?
【发布时间】:2020-02-16 06:22:49
【问题描述】:
我正在创建一个递归函数,它将查看现有列表,并创建一个新列表但没有重复项。我正在删除现有列表的头部,并检查新列表以查看它是否包含该元素。如果没有,那么我添加它,因为我知道它不是重复的。
到目前为止,我有这个代码。
let newList = []
let rec filtered inputList =
match inputList with
| [] -> []
| h::t -> if List.contains h newList then filtered t else h::(filtered t)
我知道在我的 else 语句中我没有将该元素添加到 newList。有没有办法在使用 match 语句时在 if 语句中执行多行代码?我该如何解决这个问题?
【问题讨论】:
标签:
list
recursion
functional-programming
f#
【解决方案1】:
在函数式编程中,通常你不会附加到一个列表,而是在递归函数中传递它,然后像这样在最后返回它。
let distinct list =
let rec filtered inputList outputList =
match inputList with
| [] -> outputList
| h::t -> if List.contains h outputList then (filtered t outputList) else (filtered t (h::outputList))
filtered list []
printf "%A" (distinct [1; 2; 2; 3]) // [3; 2; 1]
我尝试尽可能多地保留您的原始代码,以使差异更加明显。基本上,您从一个空列表开始递归函数,最后它返回包含唯一元素的列表。
现在这更像是一个风格问题,但我也会像这样打破 if 分开
let distinct list =
let rec filtered inputList outputList =
match inputList with
| [] -> outputList
| h::t when List.contains h outputList -> filtered t outputList
| h::t -> filtered t (h::outputList)
filtered list []
printf "%A" (distinct [1; 2; 2; 3]) // [3; 2; 1]
【解决方案2】:
dee-see 的解决方案有一个很好的特性,那就是它是尾递归的。这意味着它可以轻松处理非常大的列表。
但是,如果您是函数式编程的新手,那么从更简单的版本开始可能会更容易,它接受输入列表并直接返回输出列表作为结果。
这实际上与您所拥有的非常接近。你需要实现的逻辑是:
- 如果列表的其余部分有元素,则跳过它
- 如果该元素不在列表的其余部分,我们追加它
为此,您不需要newList。您可以简单地检查List.contains h t,因为这样可以确保您只返回每个重复元素的最后一次出现:
let rec filtered inputList =
match inputList with
| [] -> []
| h::t -> if List.contains h t then filtered t else h::(filtered t)
filtered [1;2;3;2;3;4;5;4;1]
与您的功能的唯一区别在于条件。 dee-see 的解决方案更复杂,但它达到了相同的结果。与其他答案一样,您可以使用when 更优雅地做到这一点:
let rec filtered inputList =
match inputList with
| [] -> []
| h::t when List.contains h t -> filtered t
| h::t -> h::(filtered t)
【解决方案3】:
另一种方式:
let rec distinct = function
| [] -> []
| h::t -> h :: distinct (List.filter ((<>) h) t)