首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么Scala的LazyList元素在计算后显示为未评估的元素?

为什么Scala的LazyList元素在计算后显示为未评估的元素?
EN

Stack Overflow用户
提问于 2021-05-15 19:30:32
回答 3查看 557关注 0票数 4

我对Scala完全陌生。我一直在玩LazyLists。请考虑以下几点:

代码语言:javascript
复制
val fun: Int => Int = (x: Int) => {
    println("PROCESSING...")
    x + 1
}

val lazyList = LazyList(fun(1), fun(2), fun(3))

上面的片段打印了三次"PROCESSING...",这表明计算了LazyList的所有三个元素。我发现这样的行为对于懒惰的收藏家来说是相当出乎意料的。所以,我决定印出来:

代码语言:javascript
复制
println(lazyList) // which prints "LazyList(<not computed>)".

我以为它会打印出LazytList(2, 3, 4)。(我不完全确定,但在我看来,Scala的println为惰性集合工作了,有点像GHCi中的:sprint命令,将集合分为两部分:计算的和未计算的。)

下面是我的问题,关于这个代码:

  1. 为什么没有显示作为计算结果的元素?如果它们确实未被评估,那么三重"PROCESSING..."到底是关于什么的呢?如果没有,为什么println会这么说呢?
  2. 为什么我们希望立即计算LazyList的参数(比如fun(1) )?为什么在初始化时放弃call-by-need策略?还有其他发生这种事情的情况吗?请注意,当我们使用map而不是像预期的那样手动写下来时,不会产生输出。
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-05-26 11:50:36

以下任何一项工作(不对其参数进行评估)都不使用LazyList.apply

  • LazyList.tabulate(3)(fun)
  • fun(1) #:: fun(2) #:: fun(3) #:: LazyList.empty
  • LazyList.range(1, 4).map(fun)

  1. 为什么我们希望立即计算LazyList的参数(比如fun(1) )?为什么在初始化时放弃call-by-need策略?还有其他发生这种事情的情况吗?请注意,当我们使用map而不是像预期的那样手动写下来时,不会产生输出。

我不认为应该立即计算fun(1),但这是因为您使用LazyList.apply来构造您的列表。LazyList(fun(1), fun(2), fun(3))LazyList.apply(fun(1), fun(2), fun(3))的语法糖,该函数的类型签名是def apply[A](elems: A*): LazyList[A]。注意,该函数的参数不是按名称调用的。

那么:为什么LazyList.apply没有用逐名参数来定义?

  1. Scala不可能定义带有名参数的变量函数。
  2. def apply实际上来自于SeqFactory,它被混合到大多数集合伴生对象中。LazyList伙伴对象可能是集合库中的一个位置,其中的伙伴对象apply不是一个有用的添加。

  1. 为什么没有显示作为计算结果的元素?如果它们确实未被评估,那么三重"PROCESSING..."到底是关于什么的呢?如果没有,为什么println会这么说呢?

LazyList只知道什么时候计算元素,因为您通过在LazyList中访问它们来强制它们。在使用LazyList.apply的情况下,会发生以下情况:

  1. LazyList.apply最初被调用时,将计算这些参数。
  2. LazyList.apply调用LazyList.from,它将通过延迟迭代步骤1中实现的中间Seq创建LazyList

到步骤2完成时,LazyList不知道其内容是否已被评估。此外,列表的脊柱本身是未评估的。

:sprint in GHCi并不是一个很好的比较,因为它在理解事物何时被评估时更加无所不知。它通过爬行运行时堆和在跨块运行时打印_来做到这一点。相比之下,println只是调用LazyList#toString,这是一个普通的Scala方法。

票数 4
EN

Stack Overflow用户

发布于 2021-05-15 20:12:48

尝试使用#::构造函数

代码语言:javascript
复制
scala> fun(1) #:: fun(2) #:: LazyList.empty
val res0: scala.collection.immutable.LazyList[Int] = LazyList(<not computed>)

#::采用名参数,而LazyList.apply则采用按值表示的参数。

票数 4
EN

Stack Overflow用户

发布于 2021-05-27 07:55:53

我想你是混合了两个问题。元素的计算和打印。举个更简单的例子,你有:

代码语言:javascript
复制
val lazyList = LazyList(1, 2, 3)

产出:

代码语言:javascript
复制
println(lazyList)

将是:

代码语言:javascript
复制
LazyList(<not computed>)

虽然这些值是明确计算出来的。

当打印惰性列表时,我们实际上调用了LazyList.toString,它调用addStringNoForce。正如我们所看到的,只要状态未定义,我们就能得到标准的LazyList(<not computed>)。为了使状态变为真,我们必须对成员state进行调用。有几种方法可以做到这一点。例如,在路上要调用head。请注意,这将导致第一个元素物化,而不是延迟列表的其余部分。

例如。守则:

代码语言:javascript
复制
val lazyList = LazyList(1, 2, 3)
lazyList.head
println(lazyList)

将产出:

代码语言:javascript
复制
LazyList(1, <not computed>)

以及以下各点:

代码语言:javascript
复制
val lazyList = LazyList(1, 2, 3)
lazyList.tail.head
println(lazyList)

将产出:

代码语言:javascript
复制
LazyList(1, 2, <not computed>)

您看到PROCESSING...输出的原因与LazyList完全无关。如果您调用fun(1),而不使用LazyList,则会得到相同的信息。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67550532

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档