OCaml 中最简单的 Python 枚举函数的类似物是什么?



在Python中enumerate的工作方式如下:

a_list = ['a', 'b', 'c']

for i, x in enumerate(a_list): 
print(i, x)

输出将是:

0 a
1 b
2 c

因此,enumerate实际上返回一个生成器(几乎是一个惰性序列),其中i的范围大于0,1,2,…和x依次为列表中的元素。

到目前为止,我已经给出了列表的这个定义,它不产生"生成器"。还有一个pair列表:

let enumerate (a_list: 'a list): (int * 'a) list =
let rec _enumerar (a_list: 'a list) (accum: (int * 'a) list) (i: int): (int * 'a) list =
match a_list with
| [] -> accum
| x::xs -> _enumerar  xs ((i, x)::accum) (i+1)
in
_enumerar a_list [] 0 |> List.rev

使用例子:

# enumerate ['a'; 'b'; 'c'];;
- : (int * char) list = [(0, 'a'); (1, 'b'); (2, 'c')]

有任何想法是否这个函数可能与不同的名称在标准库或在Base的任何地方实现?

使用序列或流的延迟版本呢?

最简单的OCaml等效是:

List.iteri (Printf.printf "%d %cn") ['a'; 'b'; 'c']

Python对loopenumerate使用a,因为它对高阶函数的支持有限。相比之下,OCaml中的大多数可枚举容器类型倾向于直接提供i版本的迭代器、映射和折叠。

如果你想从Python得到更直接的翻译,OCaml版本的生成器是Seq.t,因为它们都是外部迭代器:

Seq.iter (fun (i,x) -> Printf.printf "%d %cn" i x)
@@ Seq.mapi (fun i x -> i, x) 
@@ List.to_seq ['a';'b';'c']

(暂时忘记Seq.iteri也存在作为OCaml 4.14)

如果您想为列表添加索引,您可以使用List.mapi (fun i x -> (i,x))轻松地做到这一点,因此

List.mapi (fun i x -> (i,x)) ['a';'b';'c']

将返回一个新的列表[0,'a'; 1, 'b'; 2, 'c']。这基本上就是你的enumerate函数所做的。

当然,它不是一个惰性生成器。在标准库中有惰性生成器模块Seq,但它是最近才添加的,所以接口仍在开发中。或者,您可以使用Janestreet的Base(及其扩展Core)中提供的Sequence模块,例如

open Base
let enumerate xs =
Sequence.of_list xs |>
Sequence.mapi ~f:Tuple.T2.create

生成的序列是惰性的,所以函数enumerate xs是0(1),它在序列被使用之前不会迭代列表,例如,

open Base
let () = enumerate ['a'; 'b'; 'c'] |> Sequence.iter ~f:(fun (i,c) -> 
printf "%d %cn" i c
当然,使用Sequence.iteri甚至List.iteri迭代器也可以轻松地完成相同的操作。这就引出了迭代器的话题。在OCaml以及许多其他函数式编程语言中,我们使用迭代器(如itermapfold)来表示对容器的迭代。大多数任务可以很容易地使用其中一个迭代器(并从它们派生)来表示,因此很少需要使用生成大量中间数据结构的python方法。

所以从@octachron的回答看来,最简单(非懒惰)的版本,不使用Base,将是:

# let enumerate_list (lst: 'a list) = List.mapi lst ~f:( fun i x -> (i, x)) 
val enumerate_list : 'a list -> (int * 'a) list = <fun>

如果我们想要一个最接近Python生成器方法的惰性版本,使用Base.Sequence,它将是:

# open Base
# let enumerate(lst: 'a list) =  Sequence.mapi ~f:(fun i x -> (i, x)) 
(Sequence.of_list lst) ;;

这样我们就可以使

# let enumerated_seq = enumerate ['a'; 'b'; 'c']
val a : (int * char) Sequence.t = <abstr>

,如果我们想具体化一个列表

# let enumerated_list = Sequence.to_list @@ enumerate ['a'; 'b'; 'c']
val a : (int * char) list = [(0, 'a'); (1, 'b'); (2, 'c')]

最新更新