在SML中合并无限列表

时间:2013-11-02 06:04:54

标签: list zip sml

在SML中,我创建了三个无限列表,即fibonaccievenfiboddfib。现在我要做的是创建第四个列表,其中包含evenfib的前10个数字和oddfib的前10个数字,并将它们合并为一个evenfib和一个{{ {1}}使用oddfib函数创建第四个列表。

我已经编写了一个zip函数,但它不起作用。

zip

2 个答案:

答案 0 :(得分:6)

首先,您可能需要考虑简化谓词函数,因为它们不必要地冗长。在我的拙见中,这相当于更好的风格:

fun even n = n mod 2 = 0
fun odd n = n mod 2 <> 0

关于流数据类型

由于SML有严格的评估,传统的列表不会起作用。您必须首先定义自己的流数据类型。 stream是延迟列表。

你对fibs函数的定义似乎意味着存在这样的数据类型:

datatype 'a stream =  Empty | Cons of 'a  *  (unit -> 'a stream)

正如您所看到的,'a stream类型的元素可以是Empty,也可以是包含Cons类型值的'a以及能够生成的函数下一个流元素。

通过这个我们可以在评估第二个元素时有所不同,直到我们实际调用该函数。

自然数的无限流

例如,您可以定义一个无限的自然数流,如下所示:

fun from n = Cons(n, fn () => from(n +1))
val naturals = from(1)

这里自然是包含所有自然数的无限流。你可以看到Cons只包含第一个元素,第二个元素是一个函数,在评估时,它可以生成另一个stream元素,这次包含2,依此类推。

需要流函数库

显然,您不能将此数据结构与传统的列表函数一起使用。您需要编写自己的函数来处理此数据类型。

例如,您可以编写自己的take函数,该函数从流中创建有限流的n个元素:

fun take n xs =
    if n = 0
    then Empty
    else case xs of
            Empty => Empty
          | Cons(h,t) => Cons(h, fn() => take (n-1) (t()))

或者您可以创建自己的filter函数来过滤流中的元素,从而在流程中创建新流。

fun filter f xs = 
   case xs of
      Empty => Empty
    | Cons(h,t) => if f(h)
                   then Cons(h, fn () => filter f (t()))
                   else filter f (t())

你需要一个zip函数将两个流的元素压缩到另一个压缩流中,如下所示:

fun zip(xs,ys) = 
    case (xs,ys) of
        (Empty,_) => Empty
      | (_, Empty) => Empty
      | (Cons(h1,t1), Cons(h2,t2)) => Cons( (h1,h2), fn () => zip(t1(),t2()))

您甚至可能希望有一个将有限流转换为列表的函数,仅用于调试目的,因为列表在REPL中更易于阅读:

fun toList xs =
    case xs of
        Empty => []
      | Cons(h,t) => h::toList(t())

例如:

  • toList (take 10 (from 1))会将前10个自然数作为列表。
  • filter odd将生成一个只从int流中获取奇数元素的函数。
  • filter even会生成一个只从int流中获取偶数元素的函数。

Fibonacci数的无限流

假设Fibonacci数量无限:

fun fibonacci() = 
   let
      fun fib(a,b) = Cons(a+b, fn() => fib(b,a+b))
   in
      Cons(0, fn() => fib(0,1))
   end  

您现在可以使用filter oddfilter even函数仅滤除偶数或奇数Fibonacci数,然后使用zip函数将这两个结果用于获取压缩流(赔率,均匀)斐波纳契数和从生成的流中,你可以take出前10个元素......

val fibs = fibonacci()
val evens = filter even
val odds = filter odd
val zipped = zip(evens fibs, odds fibs)

...你最终可以变成这样的列表:

val r = toList (take 10 zipped)

答案 1 :(得分:2)

您正在尝试获取无限列表并将其压缩到正常的元组列表中。这个问题是普通列表无法真正处理无穷大。相反,您可以将它们压缩到您自己的列表类型中:

zip : 'a inflist * 'b inflist -> ('a * 'b) inflist

如果可以避免,请不要将HDTL(或hdtl用于内置列表)。而是模式匹配:

fun zip (CONS (a, f), CONS (b, g)) = CONS (...) (* try to fill this one in yourself *)
  | zip _ = NIL (* assuming your inflist datatype has a constructor for the
                   empty list called NIL *)