基于foldRight()的filter()溢出

时间:2017-06-24 01:59:46

标签: scala lazy-evaluation

在“Scala中的函数编程”一书的第5章的示例中,我实现了惰性列表,并通过尝试在Fibonacci系列中查找素数来测试它。书中给出的默认实现(我的实现基于)stackoverflows。但是,如果我将filter()函数的实现从基于foldRight的实现更改为使用模式匹配的实现,则会解决stackoverflow问题。为什么会出现这种情况?

1)主要功能: filter_overflow()使用foldRight和overflows实现。 filter()是使用模式匹配实现的,不会溢出。

package example

object Hello {
  import datastructures._
  import datastructures.LazyList._
  def main(args: Array[String]): Unit = {
  def fibs: LazyList[BigInt] =
      LazyList[BigInt](0, 1) append unfold[(BigInt, BigInt), BigInt]((0, 1))(_ match { case (a, b) => (a + b, (b, a + b)) })
    println(fibs.filter(_.isProbablePrime(8)).drop(BigInt(21)).headOrElse(-1))
    println(fibs.filter_overflow(_.isProbablePrime(8)).drop(BigInt(21)).headOrElse(-1))
 }
}

2)LazyList实现:

package datastructures

sealed trait LazyList[+A] {
  import LazyList.{empty, cons, join, unit}
  def toList: List[A] = this.foldRight(List[A]())(_ :: _)
  def head: A = this match {
    case Cons(h, _) => h()
  }
  def headOrElse[B >: A](default: B): B = this match {
    case Empty => default
    case Cons(h, _) => h()
  }
  def tail: LazyList[A] = this match {
    case Cons(h, t) => t()
  }
  def map[B](f: A => B): LazyList[B] =
    this.foldRight(empty[B])((a, bs) => cons(f(a), bs))
  def flatMap[B](f: A => LazyList[B]): LazyList[B] =
    join(this map f)
  def foldRight[B](z: => B)(f: (A, => B) => B): B = this match {
    case Empty => z
    case Cons(h, t) => f(h(), t().foldRight(z)(f))
  }
  def append[B >: A](that: => LazyList[B]): LazyList[B] =
    this.foldRight(that)(cons(_, _))
  @annotation.tailrec
  final def drop(n: BigInt): LazyList[A] =
    if (n <= BigInt(0)) this else this.tail.drop(n - BigInt(1))
  def take(n: BigInt): LazyList[A] =
    if (n <= BigInt(0)) empty[A]
    else cons(this.head, this.tail.take(n - BigInt(1)))
  def filter_overflow(f: A => Boolean): LazyList[A] =
    this.foldRight(empty[A])((x, xs) => if (f(x)) cons(x, xs) else xs)
  def filter(pred: A => Boolean): LazyList[A] = this match {
    case Empty => empty[A]
    case Cons(h, t) =>
      if (pred(h())) cons(h(), t() filter pred)
      else t() filter pred
  }
}
case object Empty extends LazyList[Nothing]
case class Cons[+A](
  hd: () => A,
  tl: () => LazyList[A]) extends LazyList[A]
object LazyList {
  def unit[A](a: A): LazyList[A] = LazyList(a)
  def apply[A](as: A*): LazyList[A] =
    if (as.isEmpty) empty[A]
    else cons(as.head, apply(as.tail: _*))
  def cons[A](h: => A, t: => LazyList[A]): LazyList[A] = {
    // lazy val _h: A = hd
    // lazy val _t: LazyList[A] = tl
    // Cons(() => _h, () => _t)
    Cons(() => h, () => t)
  }
  def empty[A]: LazyList[A] = Empty
  def join[A](ss: => LazyList[LazyList[A]]): LazyList[A] =
    ss.foldRight(empty[A])(_ append _)
  def map2[A, B, C](
    as: LazyList[A],
    bs: LazyList[B])(f: (A, => B) => C): LazyList[C] =
    as flatMap(x => bs flatMap(y => unit(f(x, y))))
  def unfold[S, A](s: S)(f: S => (A, S)): LazyList[A] = {
    val (a0, s0): (A, S) = f(s) 
    cons(a0, unfold(s0)(f))
  }
}

1 个答案:

答案 0 :(得分:1)

令我印象深刻的是,两者之间的主要区别在于每次递归在堆栈框架上放置了多少东西。

注意filter()如何使用foldRight无法完成谓词lambda(匿名函数),直到到达终点并且堆栈开始展开。

def foldRight[B](z: => B)(f: (A, => B) => B): B = this match {
  case Empty => z
  case Cons(h, t) => f(h(), t().foldRight(z)(f))
}
def filter(f: A => Boolean): LazyList[A] =
  this.foldRight(empty[A])((x, xs) => if (f(x)) cons(x, xs) else xs)

换句话说,foldRight(/*2 parameters*/)f(/*2 parameters*/)都会在每次迭代时都进入堆栈框架。

另一方面,没有filter()的{​​{1}}总是在递归之前完成谓词。

foldRight()

未完成的def filter(pred: A => Boolean): LazyList[A] = this match { case Empty => empty[A] case Cons(h, t) => if (pred(h())) cons(h(), t() filter pred) else t() filter pred } 也会使用cons(/*2 parameters*/)进入堆栈框架,但并非总是如此。

两者之间的差异,乘以数千次迭代,可以解释你得到的结果。