fibs :: [Int]
fibs = 0 : 1 : [ a + b | (a, b) <- zip fibs (tail fibs)]
这会生成Fibonacci序列。
我理解警卫的行为,:
,zip
和tail
,但我不明白<-
。这是做什么的?
答案 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中可用值的最后两个值。
这是计算过程:
[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
。[1, 1, 2, ??]
,即[last [take 3 fibs], last [take 2 fibs]]
,该值现在可用,因此我们可以继续使用。最后,我们得到第四个值3。看起来不熟悉吗?就像使用递归函数来计算纤维一样。我们现在让编译器本身来做这些事情(引用)。我们在这里使用惰性评估。
zip实现只是此处[last fibs values]的另一种表示形式。您只需进行一些修改即可了解fibs实现的zip版本。
haskell Wiki:lazy evaluation
表示[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 < 0
,f(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
不是尚未计算。
它“等待”(在等号的左侧)计算在右侧(等号)。