繁体   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