2017-02-24 6 views
0

を足すための一般的な方法、foldLeftfoldRightという2つの重要な方法があります。ここでfoldRightスカラ:機能programmmingでfoldLeft

sealed trait List[+A] 
case object Nil extends List[Nothing] 
case class Cons[+A](head: A, tails: List[A]) extends List[A] 

def foldRight[A, B](ls: List[A], z: B)(f: (A, B) => B): B = ls match { 
    case Nil => z 
    case Cons(x, xs) => f(x, foldRight(xs, z)(f)) 
    } 

そして、ここでの実装はfoldLeftの実装である:

@annotation.tailrec 
    def foldLeft[A, B](ls: List[A], z: B)(f: (B, A) => B): B = ls match { 
    case Nil => z 
    case Cons(x, xs) => foldLeft(xs, f(z, x))(f) 
    } 
} 

私の質問は:私は多くの文書から読み込まれ、f関数の彼ら頻繁に置くためには、次のとおりです。f: (B, A) => Bの代わりに、 f: (A, B) => B。なぜこの定義が良いのでしょうか?他の方法で使用すると、foldLeftと同じシグネチャが適用されるため、より良いでしょう。

答えて

3

foldLeftは、その横断中短所構造を「回る」ので:

foldRight(Cons(1, Cons(2, Nil)), z)(f) ~> f(1, f(2, z)) 
              ^^ 
              A B 
foldLeft(Cons(1, Cons(2, Nil)), z)(f) ~> f(f(z, 2), 1) 
             ^ ^
              B  A 

そして、それは逆の順序でコンスを訪問するので、種類も伝統的に反転しています。もちろん、引数は入れ替えることができますが、非可換的な操作をして、 "伝統的な"振る舞いを期待するなら、あなたは驚くでしょう。

1

...他の方法で使用すると、foldLeftと同じ署名が付いています。

いいえ、私はそれが良いことはないと思います。それらが同じシグネチャを持っていれば、コンパイラはあなたがそれを使うつもりならばそれを捕まえることはできませんが、間違って他のものを入力してしまいます。

B(初期値または「ゼロ」)の値がA要素のコレクションとの関係にある場合のA/B順序も便利です。

foldLeft: B-->>A, A, A, ... // (f: (B, A) => B) 
foldRight: ... A, A, A<<--B // (f: (A, B) => B) 
関連する問題