11

a -> b -> afold left が期待する関数が、ではなく型シグネチャを持っているのはなぜだろうかb -> a -> a。この背後にある設計上の決定はありますか?

たとえば、Haskell ではfoldl (\xs x -> x:xs) [] xs、短いリストではなくリストを逆にするように書く必要foldl (:) [] xsがあります (これは で可能b -> a -> aです)。一方、標準を必要とするユースケースもありますa -> b -> a。Scala では、これは appending:xs.foldLeft(List.empty[Int]) ((xs, x) => xs:+x)である可能性があり、 として記述できますxs.foldLeft(List.empty[Int]) (_:+_)

代替の型シグネチャではなく、指定された型シグネチャを必要とするユースケースが比例して多く発生しますか、それとも Haskell と Scala (およびおそらく他の多くの言語) の左折畳みの設計につながる他の決定がありますか?

4

4 に答える 4

30

概念的に言えば、右折りfoldr f z [1..4]は次の形式のリストを置き換えるとします。

  :
 / \
1   :
   / \
  2   :
     / \
    3   :
       / \
      4  []

次の形式の式の値で

  f
 / \
1   f
   / \
  2   f
     / \
    3   f
       / \
      4   z

この式を 1 行で表すと、すべての括弧が右側に関連付けられるため、right fold: という名前が付けられます(1 `f` (2 `f` (3 `f` (4 `f` z))))。左の折り目は、ある意味で右の折り目に対して二重です。特に、次のように、左の折り目に対応する図の形状が左の折り目の鏡像になるようにします。

        f
       / \
      f   4
     / \
    f   3
   / \
  f   2
 / \
z   1

この図を 1 行で書き出すと、すべての括弧が左側に関連付けられた式が得られます。これは、左側の折り畳みの名前とよく一致します。

((((z `f` 1) `f` 2) `f` 3) `f` 4)

しかし、このミラー ダイアグラムでは、折り畳みの再帰的な結果がf最初の引数として渡され、リストの各要素が 2 番目の引数として渡されることに注意してください。つまり、引数は、f正しい折り畳みとは逆の順序で渡されます。

于 2012-09-05T20:29:06.287 に答える
7

The type signature is foldl :: (a -> b -> a) -> a -> [b] -> a; it's natural for the combining function to have the initial value on the left, because that's the way it combines with the elements of the list. Similarly, you'll notice foldr has it the other way round. The complication in your definition of reverse is because you're using a lambda expression where flip would have been nicer: foldl (flip (:)) [] xs, which also has the pleasant similarity between the concepts of flip and reverse.

于 2012-09-05T20:33:54.597 に答える
4

短い形式で書くからです(a /: bs)foldLeftこれはaすべてをプッシュする演算子であるbsため、同じ方法で関数を記述するのが自然です(つまり(A,B) => A)。foldRightそれは他の順序で行われることに注意してください。

于 2012-09-05T20:00:03.767 に答える