2017-02-08 34 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原代碼。