簡體   English   中英

Scala Stream功能評估

[英]Scala Stream function evaluation

我得到以下代碼:

trait Stream[+A] {
  def uncons: Option[(A, Stream[A])]
  def foldRight[B](z: => B)(f: (A, => B) => B): B = {
    uncons.map(t => {
      f(t._1, t._2.foldRight(z)(f))
    }).getOrElse(z)
  }
  def exists(p: A => Boolean) =
    foldRight(false)((x, acc) => acc || p(x))
  def forAll(p: A => Boolean) =
    foldRight(true)((x, acc) => p(x) && acc)
}

object Stream {
  def cons[A](h: => A, t: => Stream[A]): Stream[A] =
    new Stream[A] {
      lazy val uncons = Some((h, t))
    }
}

然后,我以一種懶惰的方式創建一個Stream並調用exists方法來檢查評估了哪些stream元素:

  println(Stream.cons({println("5"); 1}, Stream.cons({println("6"); 2}, Stream.cons({println("7"); 3}, Stream.cons({println("8"); 4}, Stream.empty)))).exists(_ == 1))

我看到的是:

5
6
7
8
true

因此,盡管僅第一個就足夠了,但所有元素都進行了評估。 我似乎理解為什么exists行為會如此。

然后我運行以下代碼:

println(Stream.cons({println("13"); 1}, Stream.cons({println("14"); 2}, Stream.cons({println("15"); 3}, Stream.cons({println("16"); 4}, Stream.empty)))).forAll(_ < 2))

並查看以下內容:

13
14
false

因此,只要forAll遇到一個不令人滿意的值,它將終止遍歷。

但是,為什么forAll行為都是這樣? 它與exists之間的關鍵區別是什么?

有兩件事要考慮:

  • acc的類型
  • 布爾表達式中p(x)的順序。

怠惰

如果將acc的類型更改為B ,則兩種方法都將無法快速失敗(或短路)。 您必須知道這一點,因為您的代碼廣泛使用了惰性,但是=> B類型的變量僅在需要其值(即在某些表達式中使用)時才會得到評估。 在這種情況下, acc是在流上計算的結果的未來 僅當您嘗試着看時,才會發生這種未來。 因此,為了防止對整個流進行評估,您必須避免關注這個未來。

布爾表達式中的短路

這就是p(x)的順序重要的地方。 在表達式a && b ,如果afalse那么我們知道整個連接也是false ,因此Scala不會嘗試評估b因為它毫無意義。

兩者結合

現在,如果您的一個操作數是一個惰性表達式,會發生什么? 好吧,如果您有lazyA || b lazyA || b ,Scala從左到右讀取表達式並評估lazyA 在您的情況下, lazyA表示下一個元素和流的其余部分的累積。 因此, lazyA擴展為a0 :: lazyA1 ,后者擴展為a0 :: a1 :: lazyA2 因此,您最終將只計算布爾binop的左側部分就計算出整個流。

現在,如果您有a && lazyBa && lazyB擴展為a && (b0 :: b1 :: lazyB2) 如您在此處看到的,一旦abifalse ,它將返回而不評估語句的正確部分。 這就是您的forAll發生的情況。

如何修復

好消息是修復非常容易:只需交換p(x)acc的順序: p(x)true ,析取將返回而不評估acc ,從而停止計算。

def exists(p: A => Boolean) = foldRight(false)((x, acc) => p(x) || acc)

輸出:

5
true

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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