如何修复懒惰列表(又名“流”)的zip功能?

时间:2018-12-04 15:06:43

标签: function functional-programming ocaml

我要制作zip功能,即将两个惰性列表压缩为一个。例如,显示为类似于常规列表的懒惰列表以提高可读性,zip [1; 3; 5; 7; 9; 11] [2; 4; 6; 8]返回[1; 2; 3; 4; 5; 6 ; 7; 8; 9; 11]。

我执行此功能:

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

let zip list1 list2 =
    let rec zipHelper listA listB count = match (list1, list2) with
        | (LCons(xA, xfA), LCons(xB, xfB)) -> 
            if (count mod 2 == 0)
            then LCons(xA, function() -> zipHelper (xfA()) (xfB()) (count + 1))
            else LCons(xB, function() -> zipHelper (xfA()) (xfB()) (count + 1))
        | (LCons(x, xf), LNil) -> LCons(x, function() -> zipHelper (xf()) LNil count)
        | (LNil, LCons(x, xf)) -> LCons(x, function() -> zipHelper (xf()) LNil count)
        | (LNil, LNil) -> LNil
    in zipHelper list1 list2 0
    ;;

let rec ltake = function
    | (0, _) -> []
    | (_, LNil) -> []
    | (n, LCons(x, xf)) -> x :: ltake(n - 1, xf())
    ;;

let a = (LCons(1, function() -> LCons(3, function() -> LCons(5, function() -> LCons(7, function() -> LCons(9, function() -> LCons(11, function() -> LNil)))))));;
let b = (LCons(2, function() -> LCons(4, function() -> LCons(6, function() -> LCons(8, function() -> LNil)))));;
ltake (12, zip a b);;

ltake函数有助于测试,它会在常规列表中返回懒惰列表。现在我的函数zip返回了我[1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2]。

3 个答案:

答案 0 :(得分:2)

您在match语句中犯了一个错误。

let zip list1 list2 =
    let rec zipHelper listA listB count = match (list1, list2) with

您不希望在list1list2上进行匹配,而是在listAlistB上进行匹配。

您还应该调查这些电话,

then LCons(xA, function() -> zipHelper (xfA()) (xfB()) (count + 1))
else LCons(xB, function() -> zipHelper (xfA()) (xfB()) (count + 1))

您是否真的想将两个尾巴作为参数传递?

答案 1 :(得分:0)

这是正确的代码,可以正常工作:

let zip list1 list2 =
    let rec zipHelper listA listB count = match (listA, listB) with
        | (LCons(xA, xfA), LCons(xB, xfB)) -> 
            if (count mod 2 == 0)
            then LCons(xA, function() -> zipHelper (xfA()) listB (count + 1))
            else LCons(xB, function() -> zipHelper listA (xfB()) (count + 1))
        | (LCons(x, xf), LNil) -> LCons(x, function() -> zipHelper (xf()) LNil count)
        | (LNil, LCons(x, xf)) -> LCons(x, function() -> zipHelper (xf()) LNil count)
        | (LNil, LNil) -> LNil
    in zipHelper list1 list2 0
    ;;

更改:

将要添加的元素添加到列表后,我必须调用zipHelper函数,并使用list的尾部参数,从中获取头和第二个列表,而不仅仅是尾部。

就像以前的评论员一样,错过比赛中的名单命名。

答案 2 :(得分:0)

基于@Jorge Adriano的回答,我迅速构建了他们提出的版本:

let lnil = LNil
let lcons a l = LCons (a,l)

let rec zip = function
  | LCons (xA, xfA) as fA ->
    ( function
      | LCons(xB, xfB) ->
        lcons xA (fun () -> lcons xB (fun () -> zip (xfA ()) (xfB ())))
      | LNil -> fA
    )
  | LNil ->
    ( function
      | LCons (xB, xfB) as fB -> fB
      | LNil -> LNil
    )

我也将ltake分成了take和list转换(未实现tailrecursive),并添加了重复代码来测试corecursive数据结构:

let rec ltake n = function
    | LNil -> LNil
    | LCons(x, xf) ->
      if n == 0
      then LNil
      else lcons x (fun () -> ltake (n - 1) (xf ()))

let rec of_list = function
  | [] -> LNil
  | x :: xs -> LCons (x, fun () -> of_list xs)

let rec to_list = function
    | LNil -> []
    | LCons (x,xs) -> x :: (to_list (get xs))

let repeat n =
  let rec aux n () = LCons (n, aux n) in
  aux n ()

我们可以在原始输入上对其进行测试:

utop # ltake 12 (zip a b) |> to_list ;;
- : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 11]

并重复:

utop # ltake 12 (zip (repeat 1 ) (repeat 2)) |> to_list ;;
- : int list = [1; 2; 1; 2; 1; 2; 1; 2; 1; 2; 1; 2]