簡體   English   中英

Scala流,備忘錄和占位符語法

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM