普遍使用Core的`List.init`吗? [英] Core's `List.init` in Pervasives?
问题描述
我习惯了JaneStreet的Core
库.它的List
模块具有简洁的init
功能:
I'm used to JaneStreet's Core
library. Its List
module has a neat init
function:
List.init;;
- : int -> f:(int -> 'a) -> 'a list = <fun>
它允许您使用自定义函数初始化元素来创建列表:
It allows you to create a list with using a custom function to initialize elements:
List.init 5 ~f:(Fn.id);;
- : int list = [0; 1; 2; 3; 4]
List.init 5 ~f:(Int.to_string);;
- : string list = ["0"; "1"; "2"; "3"; "4"]
但是,Pervasives
中似乎不存在此功能,这很可悲.我是否缺少某些东西,还是必须自己实施?如果我确实需要编写它,该如何实现?
However, this function doesn't seem to exist in Pervasives
, which is sad. Am I missing something, or do I have to implement it myself? And if I do need to write it, how do I achieve this?
编辑:
我写了init
的命令式版本,但是在这种情况下不得不诉诸OCaml的命令式功能是不合适的. :(
I have written an imperative version of init
, but it doesn't feel right to have to resort to OCaml's imperative features in such a case. :(
let init n ~f =
let i = ref 0 in
let l = ref [] in
while !i < n do
l := (f !i) :: !l;
incr i;
done;
List.rev !l
;;
我已经在OCaml的GitHub上打开了拉请求,以包含此功能
I've opened a pull request on OCaml's GitHub to have this feature included.
此功能已在OCaml 4.06中发布.
The feature was released in OCaml 4.06.
推荐答案
递归实现非常简单.但是,它不是尾递归的,这意味着您将面临大列表的堆栈溢出的风险:
A recursive implementation is fairly straightforward. However, it is not tail-recursive, which means that you'll risk a stack overflow for large lists:
let init_list n ~f =
let rec init_list' i n f =
if i >= n then []
else (f i) :: (init_list' (i+1) n f)
in init_list' 0 n f
我们可以使用常用技术将其转换为尾递归版本:
We can transform it into a tail-recursive version using the usual techniques:
let init_list n ~f =
let rec init_list' acc i n f =
if i >= n then acc
else init_list' ((f i) :: acc) (i+1) n f
in List.rev (init_list' [] 0 n f)
这使用累加器,并且还需要颠倒中间结果,因为列表是反向构造的.请注意,我们也可以使用f (n-i-1)
而不是f i
来避免反转列表,但是如果f
具有副作用,这可能会导致意外的行为.
This uses an accumulator and also needs to reverse the intermediate result, as the list is constructed in reverse. Note that we could also use f (n-i-1)
instead of f i
to avoid reversing the list, but this may lead to unexpected behavior if f
has side-effects.
另一种较短的解决方案是简单地将Array.init
用作起点:
An alternative and shorter solution is to simply use Array.init
as a starting point:
let init_list n ~f = Array.(init n f |> to_list)
这篇关于普遍使用Core的`List.init`吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!