在Haskell中,使用takeWhile或在此列表理解中使用"常规"不等式有什么区别?

Dou*_*hen 13 haskell functional-programming lazy-evaluation

我正在努力学习一个Haskell(非常好),而我正在做的许多不同的事情之一是试图解决一些Project Euler问题,因为我正在测试我的勇气.

在做一些基于Fibonacci的问题时,我偶然发现并开始使用Fibonacci序列的递归无限列表版本:

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

对于其中一个PE问题,我需要提取甚至斐波纳契数小于4,000,000的子序列.我决定用列表理解来做这件事,在我玩代码时,我偶然发现了一些我不太了解的东西; 我假设我对Haskell的懒惰评估方案的掌握很弱,这使得事情复杂化.

以下理解工作正常:

[x | x <- takeWhile (<= 4000000) fibs, even x]

下一个理解永远旋转; 所以我经历并将输出返回到stdout,当它停在正确的位置时,它似乎继续永远地评估递归定义的列表,而不是在达到上限值后完成; 指示列表中的最后一项使用逗号打印但不存在其他列表项或结束方括号的事实:

[x | x <- fibs, x <= 4000000, even x]

那么各种功能使用的秘密酱究竟是什么呢?

Chr*_*lor 23

功能takeWhile不断取输入列表的元素,直到它到达第一元件不会满足谓词,然后将其停止.只要至少有一个元素不满足谓词,takeWhile就将无限列表转换为有限列表.

你的第一个表达说

继续使用此无限列表的元素,直到找到大于4,000,000的元素,然后停止.如果它是偶数,则在输出中包括每个元素.

第二个表达说

继续采用这个无限列表的元素.如果输出中的每个元素小于或等于4,000,000并且它是偶数,则将其包含在输出中.

当你观察到一个永远挂起的输出时,该函数正忙着产生更多的斐波那契数,并检查它们是否小于或等于4,000,000.它们都不是,这就是为什么没有任何东西被打印到屏幕上,但是这个函数无法知道它不会在列表中再遇到一个小数字,所以它必须继续检查.

  • 但是,一个好老师会让你看起来不透明的东西看起来很清楚.如果你发现自己在想"哦,是的,当然",那就是一个很好的解释. (9认同)