[英]Scala Streams, memoization and placeholder syntax
最近,我一直在與Scala Infinite Streams一起玩,並且注意到一種奇怪的行為。 這個想法是為了證明備忘錄可以與聲明為val
Streams一起使用。
具有以下測試套件:
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
}
關於Stream內容的所有斷言仍將保留,但是hitCounter
將僅更新一次(並以值1結尾)。
我認為Scala方面存在某種優化,這是內聯的,它可以抑制Clojure主體中的任何副作用。 任何人都可以解釋嗎?
Scala版本2.11.7
以下兩個表達式是等效的:
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>
在第一個中,方括號及其內容是對函數求值的塊,而在第二個中,方括號及其內容是作為函數結果的塊。
花括號中使用的占位符語法不是函數,而是返回結果為函數的代碼塊。 該表達式只計算一次,因此任何副作用都只會發生一次。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.