当参数是列表时,惰性求值如何工作?

Posted

技术标签:

【中文标题】当参数是列表时,惰性求值如何工作?【英文标题】:How does lazy evaluation works when the argument is a list? 【发布时间】:2018-06-18 10:10:46 【问题描述】:

据我了解,惰性求值是指参数在传递给函数之前不求值,而只有在实际使用它们的值时才求值。

但在 haskell 教程中,我看到了一个示例。

xs = [1,2,3,4,5,6,7,8] 

doubleMe(doubleMe(doubleMe(xs)))

作者说命令式语言可能会通过列表一次并制作副本然后返回它。然后它会再遍历列表两次并返回结果。

但在惰性语言中,它会首先计算

doubleMe(doubleMe(doubleMe(1)))

这将返回一个doubleMe(1),即2。然后是4,最后是8

所以它只会在列表中传递一次,并且仅在您真正需要时才传递。

这让我很困惑。为什么惰性语言不将列表作为一个整体,而是将其拆分?我的意思是我们可以在使用它之前忽略列表或表达式是什么。但是我们在使用它时需要评估整个事物,不是吗?

【问题讨论】:

【参考方案1】:

[1,2,3,4,5,6,7,8] 这样的列表只是语法糖:1:2:3:4:5:6:7:8:[]

在这种情况下,列表中的所有值都是数字常量,但我们可以定义另一个更小的列表,如下所示:

1:1+1:[]

所有的 Haskell 列表都是链表,这意味着它们有一个 head 和一个 tail。在上面的例子中,头部是1,尾部是1+1:[]

如果您只想要列表的头部,则没有理由评估列表的其余部分:

(h:_) = 1:1+1:[]

这里,h 指的是1。如果您只需要 h,则没有理由评估列表的其余部分 (1+1:[])。

这就是列表的惰性求值方式。在需要该值之前,1+1 仍然是 thunk(未计算的表达式)。

【讨论】:

如果我想要整个列表,它将以哪种方式运行? doubleMe(doubleMe(doubleMe(xs)))doubleMe(doubleMe(doubleMe(1))) doubleMe(doubleMe(doubleMe(2)))...? @user8314628 如果您还有其他问题,请在其他地方打开另一个问题,而不是在 cmets 中。 @user8314628 我同意@AJFarmar 的观点,即另一个问题可能是合适的。如果您决定提出一个新问题,我们需要 doubleMe 的定义才能有机会回答该问题。 好的,我认为这将是我最初问题的一部分。 @user8314628 当我不知道doubleMe 是什么或者它是如何实现的时候,我不知道如何回答这个问题。

以上是关于当参数是列表时,惰性求值如何工作?的主要内容,如果未能解决你的问题,请参考以下文章

惰性求值和 const 正确性问题

奇怪的 jags.parallel 错误/避免函数调用中的惰性求值

Swift 之惰性求值

渴望评估/应用顺序和惰性评估/正常顺序

从属属性中的 MATLAB 惰性求值

Python中的惰性求值