使用fold_left在OCaml中搜索具有特定长度的列表



我编写了一个函数,它通过使用模式匹配搜索int-list列表以返回具有特定长度的列表的索引:

let rec search x lst i = match lst with
| [] -> raise(Failure "Not found")
| hd :: tl -> if (List.length hd = x) then i else search x tl (i+1)
;;

例如:

utop # search 2 [ [1;2];[1;2;3] ] 0 ;;
- : int = 0

是否有一种方法可以使用fold.left编写具有相同功能的函数?

List.fold_left实际做什么?

接受一个列表、一个初始值和一个作用于该初始值和列表中第一个元素的函数(与参数的顺序相反)。如果列表为空,则返回初始值。否则,它使用该函数通过递归的方式更新初始值,并在列表的尾部工作。

let rec fold_left f init lst =
match lst with
| [] -> init
| x::xs -> fold_left f (f init x) xs

现在,在迭代时需要跟踪哪些信息?索引。简单。

但是,如果你没有找到这个长度的列表呢?你需要记录你是否找到了一个。假设我们使用索引和布尔标志的元组。

你传递给fold_left的函数只需要确定,如果匹配已经找到,没有更新是必要的。实际上我们对列表的其余部分不做任何操作。但是,如果我们没有找到匹配,那么我们需要测试当前子列表的长度并相应地更新init值。

@glennsl(在评论中)和@Chris已经解释了您可以使用List.fold_left,但它不是适合该工作的工具,因为它处理整个列表,而您希望在发现事件后停止。有解决方案,但他们并不令人满意:

  • (@Chris的解决方案:)使用一个折叠函数,一旦发现一个事件就忽略新元素:你只是在浪费时间,一无所获地遍历剩余的尾部;
  • 通过抛出和捕获异常来逃避循环:更好但更hack,您正在围绕List.fold_left的正常功能工作。

我刚才提到,在标准库中有一个几乎完美匹配您的情况的泛型函数:

val find : ('a -> bool) -> 'a list -> 'a

find f l返回列表l中满足谓词f的第一个元素,
如果列表l中没有满足f的值,则引发Not_found

然而,它不返回索引,不像你所要求的。这是标准库中经过深思熟虑的设计选择,因为列表索引效率低下(线性时间),不应该这样做。在这些警示性的话之后,如果您仍然需要索引,那么很容易编写一个泛型函数find_with_index


关于代码的另一个注释:您可以完全避免计算内部列表的长度,这要归功于以下标准函数:

val compare_length_with : 'a list -> int -> int

将列表的长度与整数进行比较。compare_length_with l len相当于compare (length l) len,除了在列表上最多迭代len次后计算停止。
从4.05.0

所以你可以用if List.compare_length_with hd x = 0代替if List.length hd = x

相关内容

  • 没有找到相关文章

最新更新