繁体   English   中英

这个haskell代码如何工作?

[英]How does this haskell code work?

我是一名新生,我正在攻读计算机科学专业。 我们正在处理Haskell,虽然我理解Haskell的想法,但我似乎无法弄清楚我们应该看到的代码片段究竟是如何工作的:

module U1 where

double x = x + x
doubles (d:ds) = (double d):(doubles ds)
ds = doubles [1..]

我承认,对于知道发生了什么事的人来说似乎相当简单,但我无法绕过它。 如果我写“take 5 ds”,它显然会回复[2,4,6,8,10]。 我没有得到的,是为什么。

这是我的思路:我打电话给ds,然后寻找双打。 因为我也提交了值[1 ..],双精度(d:ds)应该表示d = 1且ds = [2 ..],对吗? 然后我将d加倍,返回2并将其放在列表的开头(数组?)。 然后它调用自身,将ds = [2 ..]转换为d = 2和ds = [3 ..],然后再次将d加倍并再次调用自身,依此类推,直到它可以返回5个值, [2,4,6,8,10]。

首先,我的理解是对的吗? 我的思绪中是否有任何严重的错误? 第二,因为它似乎将所有加倍的d保存到列表中以便稍后调用,那么该列表的名称是什么? 我在哪里完全定义它?

在此先感谢,希望你能帮助学生理解这个x)

我认为关于doubles如何遍历无限列表的每个元素的递归/循环部分你是正确的。

现在关于

它似乎将所有加倍的d保存到列表中以便稍后调用,该列表的名称是什么? 我在哪里完全定义它?

这涉及在Haskell中称为Lazy Evaluation的功能。 该列表未预先计算并存储在任何位置。 相反,您可以想象列表是C ++中的一个函数对象,可以在需要时生成元素。 (您可能会看到的正常语言是表达式是按需评估的)。 所以,当你这样做

take 5 [1..]

[1..]可以被视为一个函数对象,当与headtake等一起使用时会生成数字。所以,

take 5 [1..] == (1 :  take 4 [2..])

这里[2..]也是一个为你提供数字的“函数对象”。 同样,你可以拥有

take 5 [1..] == (1 : 2 : take 3 [3..]) == ... (1 : 2 : 3 : 4 : 5 : take 0 [6..])

现在,我们不需要关心[6..] ,因为对于任何xs take 0 xs xs[] 因此,我们可以拥有

take 5 [1..] == (1 : 2 : 3 : 4 : 5 : [])

无需存储任何“无限”列表,如[2..] 如果您想了解Lazy计算实际上是如何发生的,可以将它们视为函数对象/生成器。

你的思路看起来是正确的。 其中唯一的微小不准确在于使用表达式来描述计算,例如“它加倍2 然后调用自身......”。 在纯函数式编程语言中,例如Haskell,实际上没有固定的评估顺序。 具体来说,在

double 1 : double [2..]

在将列表的其余部分加倍之前是否发生加倍1是未指定的。 理论结果保证了秩序确实无关紧要,因为 - 大致 - 即使你以不同的顺序评估你的表达式,你也会得到相同的结果。 我建议您使用Lambda Bubble Pop网站查看此属性:您可以按不同顺序弹出气泡以模拟任何评估顺序。 无论你做什么,你都会得到相同的结果。

请注意,由于评估顺序无关紧要,Haskell编译器可以自由选择它认为最适合您的代码的任何评估顺序。 例如,让ds定义为代码的最后一行,并考虑

take 5 (drop 5 ds)

这导致[12,14,16,18,20] 请注意,编译器不需要将前5个数字加倍,因为您正在删除它们,因此可以在完全计算之前删除它们(!!)。

如果你想进行实验,可以自己定义一个计算成本非常高的函数(比如,在递归定义后写下fibonacci )。

fibonacci 0 = 0
fibonacci 1 = 1
fibonacci n = fibonacci (n-1) + fibonacci (n-2)

然后,定义

const5 n = 5

并计算

fibonacci 100

并观察实际需要多长时间。 然后,评估

const5 (fibonacci 100)

并且看到结果立即到达 - 参数甚至没有计算(!),因为不需要它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM