【问题标题】:Merge infinite lists in SML在 SML 中合并无限列表
【发布时间】:2014-02-05 01:42:13
【问题描述】:

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

我写了一个zip函数如下,但它不起作用。

fun fib a b = CONS(a, fn () => fib b (a + b));
fun odd n = if ( n mod 2 = 1) then true else false;
fun even n = if (n mod 2 = 0) then true else false;
val fibs = fib 0 1;
fun evenfibs l = FILTER even l;
fun oddfibs l = FILTER odd l;
fun zip x = case x of (L 'a inflist , N 'b inflist) => (HD L, HD N) :: zip (TL L, TL N) | => _ nil;   

【问题讨论】:

  • 路过评论,我的小毛病:if C then true else false 只是说C 的一种复杂方式。 ;)
  • @RahulSingh,您对该页面的编辑非常不恰当。您不应该尝试这样删除其他人的作品。如果您对帖子有任何问题,请发表评论、投票或标记。编辑是为了改进帖子,而不是用填充文本替换它们。如果您对此有任何疑问,请告诉我。

标签: list zip sml


【解决方案1】:

首先,您可能需要考虑简化谓词函数,因为它们不必要地冗长。在我看来,这是等效的并且风格更好:

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,也可以是包含'a 类型值的Cons 和能够生成下一个流元素的函数。

因此,在我们实际调用函数之前,我们可以在评估第二个元素时有所不同。

自然数的无限流

例如,您可以像这样定义一个无限的自然数流:

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

这里的 naturals 是一个包含所有自然数的无限流。你可以看到 Cons 只包含第一个元素,而第二个元素是一个函数,在计算时可以生成另一个流元素,这次包含 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 流中获取偶数元素。
  • 等,

无限的斐波那契数流

假设有无限的斐波那契数流:

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 函数仅过滤掉偶数或奇数斐波那契数,然后将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)

【讨论】:

    【解决方案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 *)
    

    【讨论】:

    • 嗯。好吧,在您发布的代码中,您使用了一个名为 CONS 的构造函数,所以我假设这是您的代码中已有的东西。有两个 CONS 的部分只是模式匹配——如果我们有两个非空列表,那么我们想要返回一个非空列表,其中第一个元素是 a 和 b 的元组。
    猜你喜欢
    • 2014-12-18
    • 2011-08-30
    • 2014-11-26
    • 2015-11-08
    • 2017-05-30
    • 1970-01-01
    • 2015-09-06
    • 2016-07-29
    相关资源
    最近更新 更多