使用高阶函数编写分割函数

时间:2018-02-01 19:53:04

标签: ocaml

我试图编写一个函数,根据通过列表传递的函数来划分和删除元素。

功能类型应为:

'('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

它不会删除列表中的元素。如果有人能帮我解决这个问题,我很想知道什么有用。

2 个答案:

答案 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不是,因此对于非常大的列表来说,它肯定是值得注意的。

Performance comparison

答案 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]]