如何使将惰性列表一分为二的功能?

时间:2018-11-25 20:37:22

标签: function functional-programming ocaml

我需要创建可拆分惰性列表的函数。例如,[5; 6; 3; 2; 1]-> [5; 3; 1]和[6; 2]。这是常规列表的语法,但必须是懒惰的。函数按索引划分,第一个列表为奇数,第二个为奇数。我写这样的函数,但是我不知道如何向懒惰列表添加新元素:

let rec merge list = 
let rec mergeHelp list acc = function
    | LNil -> failwith "Empty list"
    | LCons(x, xf) ->
        if (acc mod 2 == 0)
        then LCons(x, function() -> mergeHelp (acc+1) (xf())), LCons(LNil, function() -> LNil)
        else LCons(LNil, function() -> LNil), LCons(x, function() -> mergeHelp (acc+1) (xf()))
in mergeHelp list 0
;;

1 个答案:

答案 0 :(得分:2)

我不知道您的惰性列表类型是什么,但我将从您的代码中假设它看起来像:

type 'a t = LNil | LCons of 'a * (unit -> 'a t)

请注意,这与(unit -> 'a t)'a t lazy的更典型的惰性列表不同。

有可能不会以生成它们的相同方式来使用这两个列表,我们也不想遍历所有功能运行的输入,而不会出现偶数的情况。因此,让我们编写一个将元素配对的函数:

let rec pair = function 
  | LNil -> LNil
  | LCons (fst, rest) ->
    match rest () with
    | LNil -> LCons ((fst, None), const LNil)
    | LCons (snd, rest) ->
      let later_pairs = lazy (pair rest()) in
      LCons ((fst, Some snd), fun () -> Lazy.force later_pairs)

此函数的类型为'a t -> ('a * 'a option) t,其主要功能是一旦扫描了一次,由于不重新提交输出元素,因此再次进行扫描很便宜。这些类型有点令人难过,因为实际上我们只允许None作为结果的最后一个元素中的第二个元素,但让我们忍受并继续下去。首先,我们需要一些琐碎的实用程序:

let rec map f = function
  | LNil -> LNil
  | LCons (a, r) -> LCons (f a, fun () -> map f (r ()))

let rec filter_map f = function
  | LNil -> LNil
  | LCons (x, r) ->
    let r () = filter_map f (r ()) in
    match f x with
    | None -> r ()
    | Some a -> LCons (a, r)

它们具有类型('a -> 'b) -> 'a t -> 'b t('a -> 'b option) -> 'a t -> 'b t,它们在读取类型签名时会做出最愚蠢的猜测。现在,我们可以实现所需功能:

let unmerge xs =
  let pairs = pair xs in
  (map (fun (a,_) -> a) pairs, filter_map (fun (_,b) -> b) pairs)