我最近一直在玩Scala无限流,我注意到了一个奇怪的行为。这个想法是为了证明memoization可以与声明为val的流一起工作。
具有以下测试套件:
import org.scalatest.{Matchers, FunSuite}
class StreamsSuite extends FunSuite with Matchers {
test("natural numbers stream, proving memoization") {
var hitCounter = 0
lazy val Naturals: Stream[Int] = 1 #:: Naturals.map { n =>
hitCounter += 1
n + 1
}
Naturals.take(3).toIndexedSeq should be(Seq(1, 2, 3))
hitCounter should be(2)
Naturals.take(3).toIndexedSeq
hitCounter should be(2)
Naturals.take(4).toIndexedSeq
hitCounter should be(3)
}
}一切都如预期般完美地工作着。但是,当我以以下方式更改Stream定义以使用下划线占位符语法时:
lazy val Naturals: Stream[Int] = 1 #:: Naturals.map {
hitCounter += 1
_ + 1
}关于流内容的所有断言仍然有效,但hitCounter将只更新一次(并以值1结束)。
我认为在Scala方面发生了某种优化,一种内联,它抑制了clojure主体中的任何副作用。有谁能解释一下吗?
Scala版本2.11.7
发布于 2015-11-21 08:37:52
以下两个表达式是等效的:
scala> List(1, 2, 3).map { println("foo"); _ + 1 }
foo
res0: List[Int] = List(2, 3, 4)
scala> List(1, 2, 3).map({ println("foo"); _ + 1 })
foo
res1: List[Int] = List(2, 3, 4)在第二个版本中,您所看到的效果会更清晰一些。map只是一个将函数作为参数的方法,当您给它一个包含多个表达式的块时,它将立即(并且只计算一次)该块,就像它对任何其他表达式一样。
非占位符情况的不同之处在于,箭头之后的任何副作用都发生在函数内部。请看以下两个定义:
scala> val f1: Int => Int = { println("foo"); _ + 1 }
foo
f1: Int => Int = <function1>
scala> val f2: Int => Int = i => { println("foo"); i + 1 }
f2: Int => Int = <function1>在第一个示例中,括号及其内容是一个计算结果为函数的块,而在第二个示例中,它们是函数的结果块。
发布于 2015-11-22 02:45:01
花括号中使用的占位符语法不是一个函数,而是一个代码块,它以结果的形式返回函数。表达式只计算一次,因此任何副作用都只发生一次。
https://stackoverflow.com/questions/33837651
复制相似问题