文章目录
Scala 的严格求值和惰性求值
惰性求值
对一系列的转换融合成单个函数面产生临时数据结构
通过使用非严格求值(non-strictness)
,也叫惰性求值,来实现这种自动循环的融合物。非严格求值是一项提升函数式编程效率的模块化基础技术
严格和非严格函数
非严格求值是函数的一种属性,称一个函数是非严格求值的意思是这个函数可以选择不
对它的一个或多个参数求值。相反,一个严格求值的函数总是对它的参数求值。
在 Scala 中,除非明确声明,否则任何函数都是严格求值的
thunk
通常一个表达式的未求值形式称为
thunk
使用传名函数的方式,Scala 会负责为我们将表达式包装为 thunk
对一个 val 声明的变量添加 lazy 修饰符,将导致 Scala 延迟对这个变量求值,直到它第一次被应用的时候。它会缓存结果,在后续应用的地方不会触发重复求值
Scala 中非严格求值的函数接受的参数是传名参数(by name)而非传值参数(by value)
关注分离(separation of concerns)
就是将计算的描述与实际运行分开。这也是函数式编程的主题之一
一等循环(first-class loops)
可以使用高阶函数,如 map、filter 等组合
无限流(infinite stream)与共递归(corecursive)
无限流容易写出永不结束或线程栈不安全的表达式
递归函数由不断地对更小范围的输入参数进行递归调用而结束(terminate);而共递归函数只要保持生产数据不需要结束,这意味着总是可以在一个有限的时间段里对更多的结果求值。共递归有时也被成为守护递归(guarded recursion),生产能力有时也被称为共结束(cotermination)
共递归的例子:
def unfold[A, S](z: S)(f: S => Option[(A, S)]): Stream[A] = {
f(z) match {
case Some((a, s)) => cons(a, unfold(s)(f))
case None => empty
}
}
如果能够理解unfold
,foldRight
,scanRight
,那么该部分的内容就算理解透了
def foldRight[B](z: => B)(f: (A, => B) => B): B =
this match {
case Cons(h, t) => f(h(), t().foldRight(z)(f)) // If `f` doesn't evaluate its second argument, the recursion never occurs.
case _ => z
}
def scanRight[B](z: B)(f: (A, => B) => B): Stream[B] =
foldRight((z, Stream(z)))((a, p0) => {
lazy val p1 = p0
val b2 = f(a, p1._1)
(b2, cons(b2, p1._2))
})._2