2017-02-08 37 views
0

我已经习惯了JaneStreet的Core库。它的List模块有一个整洁init功能:核心的普遍使用的`List.init`?

List.init;; 
- : int -> f:(int -> 'a) -> 'a list = <fun> 

它允许你创建一个列表,使用自定义函数初始化元素:

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,这很伤心。我错过了什么,还是我必须自己实现?如果我确实需要写它,我该如何实现呢?

编辑

我写的init当务之急版本,但感觉不对不得不求助于OCaml中的必要功能,在这种情况下。 :(

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 
;; 

编辑2:

我已经开了一个pull request上OCaml中的GitHub上有此功能包括

+0

作为一般评论:不要指望普及的任何方便。如果你想要一个完整的标准库,你将不得不使用核心或容器或电池(可能还有其他)。 – user3240588

+0

是的,我已经注意到了。 – RichouHunter

回答

1

递归实现是相当简单的。然而,这是不是尾。这意味着你将冒大堆栈溢出的风险:

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 

我们可以使用通常的技术将其转化为一个尾递归版本:

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有副作用,这可能会导致意外行为。

的替代性和更短的解决方案是简单地使用Array.init为出发点:

let init_list n ~f = Array.(init n f |> to_list) 
+0

'Array.init'是一个东西,但不是'List.init'?这是一个耻辱。 :( – RichouHunter

0

您可以从JaneStreet将代码复制,并使用它

的代码看起来的一样(但不完全一样):

let init n ~f = 
    if n < 0 then raise (Invalid_argument "init"); 
    let rec loop i accum = 
    if i = 0 then accum 
    else loop (i-1) (f (i-1) :: accum) 
    in 
    loop n [] 
;; 

你可以找到从包里面CORE_KERNEL的core_list0.ml原代码。