【问题标题】:Representing Fibonacci numbers using a list comprehension in Haskell在 Haskell 中使用列表推导表示斐波那契数
【发布时间】:2014-08-20 19:30:51
【问题描述】:

我编写了以下代码来生成包含斐波那契数列的列表。

fibonacci = [a + b | a <- 1:fibonacci, b <- 0:1:fibonacci]

我希望列表的输出是[1,2,3,5,8,13..],但是,输出不是斐波那契数列。

我不太明白为什么它不起作用。

我的理由是,如果斐波那契数是[1,2,3,5,8,13..],那么这将等于[1,1,2,3,5,8,13..] 和[0,1,1,2,3,5,8,13..] 两个列表的总和,它们相当于1:[1,2,3,5,8,13..] 和0:1:[1,2,3,5,8,13..] 或@987654328 @和0:1:fibonacci

我已经查找了实现此序列的其他方法,但是我真的很想知道为什么我的代码不起作用。

【问题讨论】:

  • fibonacci = 0:1:[a + b | (a:b:_) &lt;- iterate tail fibonacci] = 0:1:[a + b | (a,b) &lt;- zip fibonacci (tail fibonacci)].

标签: haskell list-comprehension fibonacci


【解决方案1】:

问题

与:

fibonacci = [a + b | a <- 1:fibonacci, b <- 0:1:fibonacci]

您正在生成两个列表的所有可能组合。例如:

x = [a + b | a <- [1, 2], b <- [3, 4]]

结果将是:

[1 + 3, 1 + 4, 2 + 3, 2 + 4]

Live demo

与zipWith

你可以得到最接近的是zipWith:

fibonacci :: [Int]
fibonacci = zipWith (+) (1:fibonacci) (0:1:fibonacci)

Live demo

【讨论】:

  • 当然!谢谢你。我会尽快接受答案。
【解决方案2】:

列表推导模型

  • 非确定性
  • 笛卡尔积
  • 嵌套for-loops

都是等价的。所以你的斐波那契数列是错误的,因为它计算的元素太多。在伪代码中有点像

fibonacci = 
  for i in 1:fibonacci:
    for j in 0:1:fibonacci:
      i + j

您真正想要的是将列表压缩在一起,按照斐波那契长度而不是平方的顺序执行计算。为此,我们可以使用 zipWith 并通过一些代数得到标准的“棘手的 fibo”

fibonacci = zipWith (+) (1:fibonacci) (0:1:fibonacci)
fibonacci = zipWith (+) (0:1:fibonacci) (1:fibonacci)          -- (+) is commutative
fibonacci = zipWith (+) (0:1:fibonacci) (tail (0:1:fibonacci)) -- def of tail

那我们就定义

fibonacci' = 0:1:fibonacci
fibonacci' = 0:1:zipWith (+) (0:1:fibonacci) (tail (0:1:fibonacci))
fibonacci' = 0:1:zipWith (+) fibonacci' (tail fibonacci')

这是标准与

fibonacci = drop 2 fibonacci'

您还可以使用ParallelListComprehension 扩展名,它可以让您以稍微不同的语法压缩列表推导

{-# ParallelListComp #-}
fibonacci = [a + b | a <- 1:fibonacci | b <- 0:1:fibonacci]

> take 10 fibonacci
[1,2,3,5,8,13,21,34,55,89]

【讨论】:

  • 你的代数错了。最后一步中的 0 对 1 问题。
  • 这不是“错误”,只是不相等! :) 但是,我应该更清楚,谢谢。
  • @Sam 很高兴这很有用!如果您了解有关 List monad 的更多信息,您还可以将列表理解脱糖到 do-notation 以查看“非确定性”机制。
【解决方案3】:

列表推导不是那样工作的。您已经编写了一个嵌套遍历,而您正在尝试做的是 zip。

要查看差异,请考虑:

Prelude> let fibs = [ a + b | (a,b) <- zip (1 : fibs) (0 : 1 : fibs) ]
Prelude> take 10 fibs
[1,2,3,5,8,13,21,34,55,89]

如您所愿。

Haskell 有一个语法扩展,允许并行理解,因此语法为您做了一个压缩。你可以用-XParallelListComp启用它,然后写:

Prelude> let fibs = [ a + b | a <- 1 : fibs | b <- 0 : 1 : fibs ]
Prelude> take 10 fibs
[1,2,3,5,8,13,21,34,55,89]

【讨论】:

  • 谢谢!平行列表推导设置很有趣——我以前没听说过。
  • 但如果你真的想使用它,@Sam,你应该在源文件的最顶端使用{-# LANGUAGE ParallelListComp #-} 来启用它。
【解决方案4】:

没有zip 或ParallelListComprehension 的另一种解决方案是:

> fib = 0:1:[ last x + head y | x:y:[] <- [ [take i fib, drop i fib] | i <- [1,2..] ] ]
> take 10 fib
[0,1,1,2,3,5,8,13,21,34]

【讨论】:

    猜你喜欢
    • 2018-07-03
    • 2015-08-06
    • 1970-01-01
    • 2017-07-11
    • 2011-02-17
    • 1970-01-01
    • 2017-07-08
    • 2017-01-23
    相关资源
    最近更新 更多