我编写了一个函数,它通过使用模式匹配搜索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
。