使用Haskell中的列表推导表示Fibonacci数

时间:2014-06-30 12:14:02

标签: haskell list-comprehension fibonacci

我编写了以下代码来生成包含Fibonacci数字的列表。

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

我希望列表的输出为[1,2,3,5,8,13..],但输出不是Fibonacci序列。

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

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

我已经查找了实现此序列的其他方法,但我真的很想知道为什么我的代码无法正常工作。

4 个答案:

答案 0 :(得分:8)

问题

使用:

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

答案 1 :(得分:8)

列表理解模型

  • 非决定论
  • 笛卡儿产品
  • 嵌套for - 循环

都是等价的。所以你的Fibonacci序列是错误的,因为它的计算方式太多了。在伪代码中,它有点像

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

你真正想要的是将列表压缩在一起,按照斐波那契的长度而不是方形的顺序进行计算。要做到这一点,我们可以使用zipWith,并使用一点代数,获得标准的&#34;棘手的fibo&#34;

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]

答案 2 :(得分:6)

列表理解不是那样的。您已编写嵌套遍历,而您尝试执行的操作是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有一个语法扩展,允许并行理解,所以语法为你做了一个zip。您可以使用-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]

答案 3 :(得分:0)

没有zipParallelListComprehension的另一种解决方案是:

> 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]