在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对loop
和enumerate
使用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以及许多其他函数式编程语言中,我们使用迭代器(如iter
、map
和fold
)来表示对容器的迭代。大多数任务可以很容易地使用其中一个迭代器(并从它们派生)来表示,因此很少需要使用生成大量中间数据结构的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')]