为什么 Scala 的 LazyList 的元素在计算后显示为未评估?
Why are Scala's LazyList's elements displayed as unevaluated after being computed?
我对 Scala 完全陌生。我一直在玩 LazyList
s。考虑以下因素:
val fun: Int => Int = (x: Int) => {
println("PROCESSING...")
x + 1
}
val lazyList = LazyList(fun(1), fun(2), fun(3))
上面的代码片段打印了三次 "PROCESSING..."
,这表明计算了 LazyList
的所有三个元素。我发现这种行为对于惰性集合来说是相当出乎意料的。所以,我决定打印它:
println(lazyList) // which prints "LazyList(<not computed>)".
我以为它会打印出来LazytList(2, 3, 4)
。 (我不完全确定,但在我看来,Scala 的 println
可以 用于惰性集合 类似 的 [= GHCi
中的 25=] 命令,将集合分为两部分:评估的和未评估的。)
所以,这是我关于这段代码的问题:
- 为什么没有显示已评估的元素?如果它们确实未被评估,那么这个三元组
"PROCESSING..."
是关于什么的?如果不是,为什么 println
这么说?
- 为什么我们要立即计算
LazyList
的参数,例如 fun(1)
?为什么我们在初始化时放弃了 call-by-need
策略?还有其他情况会发生这种情况吗?请注意,当我们使用 map
而不是像预期的那样手动写下时,不会产生任何输出。
尝试使用 #::
构造函数
scala> fun(1) #:: fun(2) #:: LazyList.empty
val res0: scala.collection.immutable.LazyList[Int] = LazyList(<not computed>)
#::
采用名称参数,不像 LazyList.apply
采用值。
而不是使用 LazyList.apply
,以下任何工作(不评估他们的论点):
LazyList.tabulate(3)(fun)
fun(1) #:: fun(2) #:: fun(3) #:: LazyList.empty
LazyList.range(1, 4).map(fun)
- Why do we want
LazyList
's arguments like fun(1)
to be computed right away? Why do we cast away the call-by-need
strategy when initializing? Are there any other cases where such a thing happens? Note that no output is produced when we use map
instead of writing this down manually, as expected.
我不认为立即计算 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
没有用按名称调用参数定义?
- 在 Scala 中不可能定义带有名字参数的可变参数函数
def apply
实际上来自 SeqFactory
,它混入了大多数集合伴生对象中。 LazyList
伴生对象可能是集合库中伴生对象 apply
没有帮助的地方。
- Why are no elements displayed as evaluated? If they are indeed unevaluated, what was this triple
"PROCESSING..."
thing about? If not, why does println
claim so?
LazyList
只知道元素何时被评估,因为您通过在 LazyList
中访问它们来强制它们 。在使用 LazyList.apply
的情况下,会发生以下情况:
- 在最初调用
LazyList.apply
时评估参数
LazyList.apply
调用 LazyList.from
,它将通过延迟迭代步骤 1 中实现的中间 Seq
来创建 LazyList
到步骤 2 完成时,LazyList
不知道其内容已被计算。此外,列表的书脊本身是未评估的。
GHCi 中的 :sprint
不是一个很好的比较,因为它在理解事物何时被评估方面更加无所不知。它通过爬取运行时堆并在遇到 thunk 时打印 _
来实现这一点。相比之下,println
只是调用 LazyList#toString
,这是一个常规的 Scala 方法。
我认为您混淆了 2 个问题。元素的计算,并打印它们。举一个更简单的例子,你有:
val lazyList = LazyList(1, 2, 3)
输出:
println(lazyList)
将是:
LazyList(<not computed>)
尽管这些值是明确计算出来的。
在打印惰性列表时,我们实际上是在调用LazyList.toString
which calls addStringNoForce
. As we can see, as long as the state is not defined, we get the standard LazyList(<not computed>)
. In order to get the state to be true, we must cause a call to the member state
. There are several ways to do that. On way for example is to call head
。请注意,这将导致第一个元素被具体化,而不是惰性列表的其余部分。
例如。代码:
val lazyList = LazyList(1, 2, 3)
lazyList.head
println(lazyList)
将输出:
LazyList(1, <not computed>)
以及以下内容:
val lazyList = LazyList(1, 2, 3)
lazyList.tail.head
println(lazyList)
将输出:
LazyList(1, 2, <not computed>)
您看到 PROCESSING...
输出的原因与 LazyList
完全无关。如果你调用 fun(1)
而没有 LazyList
,你得到的结果是一样的。
我对 Scala 完全陌生。我一直在玩 LazyList
s。考虑以下因素:
val fun: Int => Int = (x: Int) => {
println("PROCESSING...")
x + 1
}
val lazyList = LazyList(fun(1), fun(2), fun(3))
上面的代码片段打印了三次 "PROCESSING..."
,这表明计算了 LazyList
的所有三个元素。我发现这种行为对于惰性集合来说是相当出乎意料的。所以,我决定打印它:
println(lazyList) // which prints "LazyList(<not computed>)".
我以为它会打印出来LazytList(2, 3, 4)
。 (我不完全确定,但在我看来,Scala 的 println
可以 用于惰性集合 类似 的 [= GHCi
中的 25=] 命令,将集合分为两部分:评估的和未评估的。)
所以,这是我关于这段代码的问题:
- 为什么没有显示已评估的元素?如果它们确实未被评估,那么这个三元组
"PROCESSING..."
是关于什么的?如果不是,为什么println
这么说? - 为什么我们要立即计算
LazyList
的参数,例如fun(1)
?为什么我们在初始化时放弃了call-by-need
策略?还有其他情况会发生这种情况吗?请注意,当我们使用map
而不是像预期的那样手动写下时,不会产生任何输出。
尝试使用 #::
构造函数
scala> fun(1) #:: fun(2) #:: LazyList.empty
val res0: scala.collection.immutable.LazyList[Int] = LazyList(<not computed>)
#::
采用名称参数,不像 LazyList.apply
采用值。
而不是使用 LazyList.apply
,以下任何工作(不评估他们的论点):
LazyList.tabulate(3)(fun)
fun(1) #:: fun(2) #:: fun(3) #:: LazyList.empty
LazyList.range(1, 4).map(fun)
- Why do we want
LazyList
's arguments likefun(1)
to be computed right away? Why do we cast away thecall-by-need
strategy when initializing? Are there any other cases where such a thing happens? Note that no output is produced when we usemap
instead of writing this down manually, as expected.
我不认为立即计算 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
没有用按名称调用参数定义?
- 在 Scala 中不可能定义带有名字参数的可变参数函数
def apply
实际上来自SeqFactory
,它混入了大多数集合伴生对象中。LazyList
伴生对象可能是集合库中伴生对象apply
没有帮助的地方。
- Why are no elements displayed as evaluated? If they are indeed unevaluated, what was this triple
"PROCESSING..."
thing about? If not, why doesprintln
claim so?
LazyList
只知道元素何时被评估,因为您通过在 LazyList
中访问它们来强制它们 。在使用 LazyList.apply
的情况下,会发生以下情况:
- 在最初调用
LazyList.apply
时评估参数 LazyList.apply
调用LazyList.from
,它将通过延迟迭代步骤 1 中实现的中间
Seq
来创建 LazyList
到步骤 2 完成时,LazyList
不知道其内容已被计算。此外,列表的书脊本身是未评估的。
:sprint
不是一个很好的比较,因为它在理解事物何时被评估方面更加无所不知。它通过爬取运行时堆并在遇到 thunk 时打印 _
来实现这一点。相比之下,println
只是调用 LazyList#toString
,这是一个常规的 Scala 方法。
我认为您混淆了 2 个问题。元素的计算,并打印它们。举一个更简单的例子,你有:
val lazyList = LazyList(1, 2, 3)
输出:
println(lazyList)
将是:
LazyList(<not computed>)
尽管这些值是明确计算出来的。
在打印惰性列表时,我们实际上是在调用LazyList.toString
which calls addStringNoForce
. As we can see, as long as the state is not defined, we get the standard LazyList(<not computed>)
. In order to get the state to be true, we must cause a call to the member state
. There are several ways to do that. On way for example is to call head
。请注意,这将导致第一个元素被具体化,而不是惰性列表的其余部分。
例如。代码:
val lazyList = LazyList(1, 2, 3)
lazyList.head
println(lazyList)
将输出:
LazyList(1, <not computed>)
以及以下内容:
val lazyList = LazyList(1, 2, 3)
lazyList.tail.head
println(lazyList)
将输出:
LazyList(1, 2, <not computed>)
您看到 PROCESSING...
输出的原因与 LazyList
完全无关。如果你调用 fun(1)
而没有 LazyList
,你得到的结果是一样的。