我试图编写一个函数,根据通过列表传递的函数来划分和删除元素。
功能类型应为:
'('a -> bool) -> a list -> 'a list list
并按divide_list (fun x -> x mod 3 = 0) [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
哪个应评估为[[1; 2]; [4; 5]; [7; 8]; [10]]
。
我目前拥有的是
let divide_list cond lst =
let f elem (t, f) =
if cond elem then
( elem :: t, f)
else
(t, elem ::f)
in
List.fold_right f lst ([] ,[])
它给了我一种('a -> bool) -> 'a list -> 'a list * 'a list
它不会删除列表中的元素。如果有人能帮我解决这个问题,我很想知道什么有用。
答案 0 :(得分:2)
这部分是核心:
if cond elem then
(elem :: t, f)
else
(t, elem ::f)
您在此处执行的操作是将条件为真的所有元素分配给一个列表,将其为false的所有元素分配给另一个列表。因此,您只需将列表分为匹配的列表和不匹配的列表,而不是拆分并丢弃匹配的列表。
所以你要做的第一件事就是不要添加与任何列表匹配的元素。相反,我建议你使用第一个列表作为"当前"列表中添加了不匹配的元素,并使用第二个列表存储"已完成的"名单。也就是说,元组的类型为'a list * |a list list
,可以将其解构为(current, completed)
。
当您遇到符合条件的元素时,您需要将current
列表附加到completed
列表,并创建一个新的空current
列表以开始附加元素
完成弃牌后,您还必须将最后current
列表附加到completed
列表(如果它不是空的,可能),然后再返回。
希望有所帮助!
编辑:
由于@Goswin提供了一些代码,我想我会再次对其进行编码,看看它是如何比较的:
let divide_list cond lst =
let f elem (current, completed) =
if cond elem then
([], current :: completed)
else
(elem :: current, completed)
in let (current, completed) = List.fold_right f lst ([] ,[])
in current :: completed
使用fold_right
更简单,更接近您自己的代码,并且在小型列表的性能方面似乎没有显着差异。但是,由于fold_left
是尾递归的,而fold_right
不是,因此对于非常大的列表来说,它肯定是值得注意的。
答案 1 :(得分:1)
最好的办法是将列表折叠成一个'已完成段的列表列表'和'当前正在运行的段的列表。该条件用于决定何时开始新的段运行。请注意,我正在反向收集结果的项目,并在每个部分完成时使用List.rev以正确的顺序获取它们。
let divide_list cond lst =
let (segments, segment) =
List.fold_left
(fun (segments, segment) x ->
if cond x
then ((List.rev segment)::segments, [])
else (segments, x::segment))
([], [])
lst
in
List.rev ((List.rev segment)::segments)
这给出了:
val divide_list : ('a -> bool) -> 'a list -> 'a list list = <fun>
# divide_list (fun x -> x mod 3 = 0) [1; 2; 3; 4; 5; 6; 7; 8; 9; 10];;
- : int list list = [[1; 2]; [4; 5]; [7; 8]; [10]]