了解Haskell的斐波那契

时间:2010-03-08 19:37:55

标签: haskell list-comprehension

fibs :: [Int]
fibs = 0 : 1 : [ a + b | (a, b) <- zip fibs (tail fibs)]

这会生成Fibonacci序列。

我理解警卫的行为,:ziptail,但我不明白<-。这是做什么的?

11 个答案:

答案 0 :(得分:14)

由于赞成票,我对答案作了评论。

你看到的不是守卫,而是list comprehension。对于初学者来说,将其视为表达数学集符号的方式,如A = {x | x元素N}这意味着以下几点:集合A是所有自然数的集合。在列表理解中,[x | x <- [1..] ]

您还可以对数字使用约束:[x | x <- [1..], x `mod` 2 == 0 ]以及许多其他内容。

有很多好的haskell turorials可以覆盖列表理解,甚至还有关于haskell资源的StackOverflow问题。

答案 1 :(得分:10)

唯一棘手的问题是zip fibs (tail fibs)zip只是从每个参数中创建成对列表。所以,如果您有两个这样的列表:

[ 1, 2, 3, 4 ]
[ "a", "b", "c", "d" ]

压缩它们会:

[ (1,"a"), (2,"b"), (3,"c"), (4,"d") ]

左箭头(分配到解构模式)只提取配对元素,以便将它们加在一起。压缩的两个列表是fibs(tail fibs) - 换句话说,斐波那契序列,斐波纳契序列偏移1个元素。 Haskell是懒惰评估的,所以它可以计算列表,但需要很多元素。这也适用于拉链。

答案 2 :(得分:4)

让我们把它展开。

zip从两个列表的内容中创建对。因此,第一对zip fibs (tail fibs)给出的是(0, 1),其加起来为1.所以现在列表为[0,1,1]。我们现在知道列表中的三个元素,因此列表理解可以继续,抓住列表中的下一个项目和尾部的下一个项目,这将(1,1) - 添加在一起,制作2.然后我们得到下一个对,即(1,2),在序列3中生成下一个数字。这可以无限延续,因为理解总是会提供足够的项目。

答案 3 :(得分:2)

对于它的价值,我发现以下版本更容易理解:

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

答案 4 :(得分:2)

函数式编程的一个优点是,您可以手动计算表达式,就像数学问题一样:

vertexMarkerClass

此处fibs = 0 : 1 : [ a + b | (a, b) <- zip fibs (tail fibs)] = 0 : 1 : [ a + b | (a, b) <- zip [0, 1, ??] (tail [0, 1, ??])] 是尚未评估的部分。我们将继续填写。

??

请注意,我暂不对 = 0 : 1 : [ a + b | (a, b) <- zip [0, 1, ??] [1, ??])] = 0 : 1 : [ a + b | (a, b) <- (0, 1) : zip [1, ??] [??]] 进行评估,因为此处未给出其定义,并且详细信息与当前问题并未真正密切相关。这是我用来表示每对数字由zip创建并由列表理解消耗的符号。

zip

现在我们知道 = 0 : 1 : 0+1 : [ a + b | (a, b) <- zip [1, ??] [??]] = 0 : 1 : 1 : [ a + b | (a, b) <- zip [1, ??] [??]] 中的下一个元素是??

1

下一个元素是2:

     = 0 : 1 : 1 : [ a + b | (a, b) <- zip [1, 1, ??] [1, ??]]
     = 0 : 1 : 1 : [ a + b | (a, b) <- (1, 1) : zip [1, ??] [??]]
     = 0 : 1 : 1 : 1+1 : [ a + b | (a, b) <- zip [1, ??] [??]]
     = 0 : 1 : 1 : 2 : [ a + b | (a, b) <- zip [1, ??] [??]]

冲洗并重复。

答案 5 :(得分:1)

括号中的列表理解:

[ a + b | (a, b) <- zip fibs (tail fibs)]

返回包含输出(a + b)的列表,其中变量a和b来自

的结果
zip fibs (tail fibs)

答案 6 :(得分:0)

它定义了这个流图

           .----->>------->>----.
          /                      \
         /                       /     
         \                      /        
     <---- 0 <---- 1 ---<<--- (+)       
                 /              \         
                 \               \         
                  \              /       
                   *---->>------*       

在生成时从其自身获取新输入,但始终在生产点之前的一个和两个位置,保持两个&#34;后退指针&#34;进入序列。这反映在定义fibs = 0:1:[ a+b | a <- fibs | b <- tail fibs]中,具有并行列表推导(:set -XParallelListComp等)。

由于它仅使用其最后的两个元素,因此它等同于

    map fst . iterate (\(a, b) -> (b, a+b)) $ (0,1)

答案 7 :(得分:0)

这里的关键概念是惰性评估,这意味着如果值是正确的,那么无需进一步计算就可以将其获取,即我已经获得了价值并且工作已经完成,我现在不需要暂时计算未来的价值。如果该值不可用,则只需对其进行计算即可,当然它是惰性的,因此它不会打扰计算下一个所需的值。

我写了另一种实现方式来说明这一点,并使用??作为值占位符,如果需要,则需要对其进行计算。

fibs = 1:1:[ y!!1 + y!!0 | x<-[2..], y <- [[last (take x fibs), last (take (x-1) fibs)]] ]

我用x表示fib中的可用值数量(不需要再次计算),而y是[[[last fib values]]嵌套列表。其内部列表包含fib中可用值的最后两个值。

这是计算过程:

  1. x == 2,所以y为[last (take 2 fibs), last (take 1 fibs)]。懒惰的评估使我们仅采用可用的值,而在将来不必担心令人担忧的值。它告诉我先获取fib的2和1值,然后分别在1,1和1处。y现在是[last [1,1], last [1]],即[1,1],要获得最终值,需要计算{{ 1}}。很明显,最终值是2。所以,现在fib是y!!1 + y!!0
  2. x == 3,与步骤1相同。y为[1, 1, 2, ??],即[last [take 3 fibs], last [take 2 fibs]],该值现在可用,因此我们可以继续使用。最后,我们得到第四个值3。
  3. 就是这样,只需重复上述步骤,就可以得到所有值,即使它是无限的

看起来不熟悉吗?就像使用递归函数来计算纤维一样。我们现在让编译器本身来做这些事情(引用)。我们在这里使用惰性评估。

zip实现只是此处[last fibs values]的另一种表示形式。您只需进行一些修改即可了解fibs实现的zip版本。

  1. haskell Wiki:lazy evaluation

  2. 表示[last [1,1,2], last [1,1]]符号:List comprehension

答案 8 :(得分:0)

我更喜欢更通用的

fib = 1 : [ x + y | (x, y) <- zip fib (0 : fib) ]

这最接近地模拟了人们如何在生成函数方面理解斐波那契数列。如果 f(n) = 0 代表 n < 0f(0) = 1,而 f(n) = a*f(n-1) + b*f(n-2) 代表 n > 0,那么我们希望能够写出单行

f(n) = 1_0 + a*f(n-1) + b*f(n-2)

让读者知道我们的意思。不幸的是,这需要一些未说明的约定才能有意义。

使用生成函数 g(t) = f(0) + f(1)t + f(2)t^2 + ... 我们可以写出方程

g(t) = 1 + a t g(t) + b t^2 g(t)

以封闭形式轻松求解 g(t)

g(t) = 1 / (1 - a t - b t^2)

Haskell 代码

g = 1 : [ a*x + b*y | (x, y) <- zip g (0 : g) ]

实现相同的等式。

答案 9 :(得分:-1)

解析器如何知道(a,b)会发生什么?

编辑:感谢ViralShah,我会减少这一点。 “&lt; - ”告诉解析器将右侧“zip fibs(tail fibs)”的对列表分配给左侧“(a,b)”。

答案 10 :(得分:-1)

我还是不明白。我喜欢这个答案:https://stackoverflow.com/a/42183415/246387(来自code-apprentice)。

但我不明白这一行是怎么回事:

= 0 : 1 : 1 : [ a + b | (a, b) <- zip [1, ??] [??]]

它转向这个:

= 0 : 1 : 1 : [ a + b | (a, b) <- zip [1, 1, ??] [1, ??]]

除此之外,我还有其他困扰我的事情:

如果我根本没有fib,我怎么能在列表理解中使用fib(所以看来,但我确定我错了),因为fib不是尚未计算。 它“等待”(在等号的左侧)计算在右侧(等号)。