Scala懒惰评估和应用功能 [英] Scala lazy evaluation and apply function
问题描述
我正在按照一本书的示例在Scala中使用惰性评估来实现Steam类.
I'm following a book's example to implement a Steam class using lazy evaluation in Scala.
sealed trait Stream[+A]
case object Empty extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]
object Stream {
def cons[A](hd: => A, tl: => Stream[A]): Stream[A] = {
lazy val head = hd
lazy val tail = tl
Cons(() => head, () => tail)
}
def empty[A]: Stream[A] = Empty
def apply[A](as: A*): Stream[A] = {
if (as.isEmpty) empty else cons(as.head, apply(as.tail: _*))
}
}
然后我使用一个简单的函数来测试它是否正常工作
Then I used a simple function to test if it's working
def printAndReturn: Int = {
println("called")
1
}
然后我像下面这样构造Stream:
Then I construct Stream like the following:
println(s"apply: ${
Stream(
printAndReturn,
printAndReturn,
printAndReturn,
printAndReturn
)
}")
输出如下:
called
called
called
called
apply: Cons(fpinscala.datastructures.Stream$$$Lambda$7/1170794006@e580929,fpinscala.datastructures.Stream$$$Lambda$8/1289479439@4c203ea1)
然后我使用cons
构造了Stream:
Then I constructed Stream using cons
:
println(s"cons: ${
cons(
printAndReturn,
cons(
printAndReturn,
cons(printAndReturn, Empty)
)
)
}")
输出为:
cons: Cons(fpinscala.datastructures.Stream$$$Lambda$7/1170794006@2133c8f8,fpinscala.datastructures.Stream$$$Lambda$8/1289479439@43a25848)
所以这是两个问题:
- 使用apply函数构造Stream时,将评估所有
printAndReturn
.这是因为对apply(as.head, ...)
的递归调用会评估每个头吗? - 如果第一个问题的答案是正确的,那么如何更改
apply
使其不强制评估?
- When constructing Stream using the apply function, all
printAndReturn
are evaluated. Is this because the recursive call toapply(as.head, ...)
evaluates every head? - If the answer to the first question is true, then how do I change
apply
to make it not force evaluation?
推荐答案
-
否.如果在
println
上放置一个断点,您会发现在第一次创建Stream
时实际上正在调用该方法.实际上,行Stream(printAndReturn, ...
会调用您的方法,无论您将其放置在何处多次.为什么?考虑cons
和apply
的类型签名:
No. If you put a breakpoint on the
println
you'll find that the method is actually being called when you first create theStream
. The lineStream(printAndReturn, ...
actually calls your method however many times you put it there. Why? Consider the type signatures forcons
andapply
:
def cons[A](hd: => A, tl: => Stream[A]): Stream[A]
vs:
def apply[A](as: A*): Stream[A]
请注意,cons
的定义的参数标记为=> A
.这是一个按名称的参数.声明这样的输入会使它变得很懒,从而将其求值延迟到实际使用之前.因此,您的println
将永远不会使用cons
来调用.将此与apply
进行比较.您没有使用by name参数,因此传递给该方法的所有内容都会自动进行评估.
Note that the definition for cons
has its parameters marked as => A
. This is a by-name parameter. Declaring an input like this makes it lazy, delaying its evaluation until it is actually used. Hence your println
will never get called using cons
. Compare this to apply
. You're not using a by name parameter and therefore anything that gets passed in to that method will automatically get evaluated.
不幸的是,到目前为止,还没有一种超级简单的方法.您真正想要的是类似def apply[A](as: (=>A)*): Stream[A]
的东西,但是不幸的是,Scala不支持按名称参数使用vararg.有关如何解决此问题的一些想法,请参见此答案.一种方法是在创建Stream时只包装函数调用:
Unfortunately there isn't a super easy way as of now. What you really want is something like def apply[A](as: (=>A)*): Stream[A]
but unfortunately Scala does not support vararg by name parameters. See this answer for a few ideas on how to get around this. One way is to just wrap your function calls when creating the Stream:
Stream(
() => printAndReturn,
() => printAndReturn,
() => printAndReturn,
() => printAndReturn)
然后将延迟评估.
这篇关于Scala懒惰评估和应用功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!