foldRight效率?

时间:2012-06-12 20:53:44

标签: scala

我听说foldLeft在大多数操作中效率更高,但Scala School(来自Twitter)给出了以下示例。有人可以分析它的效率吗?我们应该使用foldLeft实现相同的操作吗?

val numbers = List(1,2,3,4,5,...10)
def ourMap(numbers: List[Int], fn: Int => Int): List[Int] = {
    numbers.foldRight(List[Int]()) { (x: Int, xs: List[Int]) =>
    fn(x) :: xs
  }
}

scala> ourMap(numbers, timesTwo(_))
res0: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

2 个答案:

答案 0 :(得分:15)

从文档中可以看出,List的foldRight和foldLeft方法在​​LinearSeqOptimized中定义。所以看一下来源:

override /*TraversableLike*/
def foldLeft[B](z: B)(f: (B, A) => B): B = {
  var acc = z
  var these = this
  while (!these.isEmpty) {
    acc = f(acc, these.head)
    these = these.tail
  }
  acc
}

override /*IterableLike*/
def foldRight[B](z: B)(f: (A, B) => B): B =
  if (this.isEmpty) z
  else f(head, tail.foldRight(z)(f))

因此foldLeft使用while循环,foldRight使用简单递归方法。特别是它不是尾递归的。因此foldRight具有创建新堆栈帧的开销,并且如果您在长列表上尝试它,则倾向于溢出堆栈(尝试,例如((1 to 10000).toList :\ 0)(_+_)。Boom!但它没有{ {1}},因为toList的{​​{1}}通过反转向左折叠来工作。

那为什么不总是使用Range?对于链表,右侧折叠可以说是更自然的功能,因为链接列表需要以相反的顺序构建。您可以使用foldRight作为上述方法,但最后需要foldLeft输出。 (不要尝试在左侧折叠中附加列表,因为复杂度为O(n平方)。)

至于哪个更快,foldLeftreverse + foldRight,我进行了一次简单的测试,foldLeft的列表速度提高了10%到40% 。这必须是List的foldRight按原样实现的原因。

答案 1 :(得分:-2)

foldRight反转列表并应用foldLeft。