9

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)
4

2 に答える 2

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!を試してみてください。ただし、は左折りを逆にすることで機能するtoListため、RangefoldRight

では、なぜ常に使用しないのfoldLeftですか?リンクリストの場合、リンクリストは逆の順序で作成する必要があるため、右折りは間違いなくより自然な機能です。上記の方法で使用できますが、最後に出力foldLeftする必要があります。reverse(複雑さはO(n-squared)であるため、左折りのリストに追加しようとしないでください。)

実際にはどちらが速いか、foldRightまたはfoldLeft+reverseについては、簡単なテストを実行し、foldRightリストの方が10〜40%高速です。これが、ListのfoldRightがそのまま実装されている理由であるに違いありません。

于 2012-06-12T22:47:14.867 に答える
-2

foldRightはリストを反転し、foldLeftを適用します。

于 2012-06-12T21:18:03.770 に答える