フォールドとリデュースの違いは?


121

F#を学習しようとしましたが、フォールドリデュースを区別しようとすると混乱しました。Foldも同じように見えますが、追加のパラメーターを受け取ります。これらの2つの機能が存在する正当な理由はありますか、それとも背景が異なる人々に対応するためにありますか?(例:文字列およびC#の文字列)

これは、サンプルからコピーしたコードスニペットです。

let sumAList list =
    List.reduce (fun acc elem -> acc + elem) list

let sumAFoldingList list =
    List.fold (fun acc elem -> acc + elem) 0 list

printfn "Are these two the same? %A " 
             (sumAList [2; 4; 10] = sumAFoldingList [2; 4; 10])

1
あなたは、reduceとfoldをお互いに関して書くことfold f a lができますreduce f a::l。例えば、のように書くことができます。
Neil

9
@ニール-のfold観点からの実装reduceはそれよりも複雑です-のアキュムレータのfoldタイプは、リスト内のもののタイプと同じである必要はありません!
Tomas Petricek 2012年

@TomasPetricek私の間違い、私はもともと逆に書くつもりでした。
Neil

回答:


171

Foldreduce入力リストの最初の要素を初期アキュムレータ値として使用しながら、アキュムレータの明示的な初期値を取ります。

これは、アキュムレータとその結果の型がリスト要素の型と一致する必要があることを意味しますがfold、アキュムレータは個別に提供されるため、それらは異なる場合があります。これはタイプに反映されます。

List.fold : ('State -> 'T -> 'State) -> 'State -> 'T list -> 'State
List.reduce : ('T -> 'T -> 'T) -> 'T list -> 'T

さらにreduce、空の入力リストで例外をスローします。


したがって、基本的には行う代わりにfold、その初期値をリストの先頭に追加して実行できますreduceか?そのときのポイントは何foldですか?
パセリエ2017

2
@Pacerier-foldのアキュムレータ関数には異なるタイプがあります:'state -> 'a -> 'statefold 'a -> 'a -> 'aとreduceの場合、reduceは結果のタイプをエレメントタイプと同じに制約します。以下のTomas Petricekの回答を参照してください。
リー

178

リーが言ったことに加えて、あなたはreduceに関して定義することができますfoldが、(簡単に)逆の方法では定義できません:

let reduce f list = 
  match list with
  | head::tail -> List.fold f head tail
  | [] -> failwith "The list was empty!"

foldアキュムレータの明示的な初期値をとるという事実は、fold関数の結果がリスト内の値のタイプとは異なるタイプを持つ可能性があることも意味します。たとえば、タイプのアキュムレータを使用stringして、リスト内のすべての数値をテキスト表現に連結できます。

[1 .. 10] |> List.fold (fun str n -> str + "," + (string n)) ""

を使用する場合reduce、アキュムレータのタイプはリストの値のタイプと同じです。つまり、数値のリストがある場合、結果は数値である必要があります。前のサンプルを実装するには、数値をstring最初に変換してから累積する必要があります。

[1 .. 10] |> List.map string
          |> List.reduce (fun s1 s2 -> s1 + "," + s2)

2
実行時にエラーが発生するようにreduceを定義するのはなぜですか?
Fresheyeball 2016

レデュースの一般性に関する注記の+1 fold' & its ability to express 。一部の言語には構造キラリティーの概念(私が見ているハスケル)があります。このwiki(en.wikipedia.org/wiki/Fold_%28higher-order_function)に視覚的に描かれた状態で左または右に折りたたむことができます。識別構造を使用すると、他の2つの「基本的な」FP演算子(フィルターとfmap)も、既存の「フォールド」ファーストクラス言語構造(これらはすべて同型構造)で実装できます。(cs.nott.ac.uk/~pszgmh/fold.pdf)参照:HoTT、プリンストン(このコメントセクションは含めるには小さすぎます。)
Andrew

好奇心から..これは、型と例外に関する仮定が少ないため、パフォーマンスをフォールドよりも速く低下させますか?
sksallaj

19

それらの署名を見てみましょう:

> List.reduce;;
val it : (('a -> 'a -> 'a) -> 'a list -> 'a) = <fun:clo@1>
> List.fold;;
val it : (('a -> 'b -> 'a) -> 'a -> 'b list -> 'a) = <fun:clo@2-1>

重要な違いがいくつかあります。

  • 一方でreduceのみの要素の一つのタイプの作品、アキュムレータとリストの要素はfold、異なるタイプである可能性があります。
  • ではreducef最初のリスト要素から始まるすべてのリスト要素に関数を適用します。

    f (... (f i0 i1) i2 ...) iN

    ではfoldfアキュムレータから始めて適用しますs

    f (... (f s i0) i1 ...) iN

したがって、reduce結果はArgumentException空のリストになります。さらに、foldより一般的ですreduce。あなたが使用することができますfold実装しreduceやすいです。

場合によっては、使用するreduce方が簡潔です:

// Return the last element in the list
let last xs = List.reduce (fun _ x -> x) xs

適切なアキュムレータがない場合は、さらに便利です。

// Intersect a list of sets altogether
let intersectMany xss = List.reduce (fun acc xs -> Set.intersect acc xs) xss

一般foldに、任意のタイプのアキュムレータを使用すると、より強力になります。

// Reverse a list using an empty list as the accumulator
let rev xs = List.fold (fun acc x -> x::acc) [] xs

18

foldreduce。よりはるかに価値のある機能です。に関して、さまざまな関数を定義できますfold

reduceはのサブセットにすぎませんfold

折り目の定義:

let rec fold f v xs =
    match xs with 
    | [] -> v
    | (x::xs) -> f (x) (fold f v xs )

foldで定義された関数の例:

let sum xs = fold (fun x y -> x + y) 0 xs

let product xs = fold (fun x y -> x * y) 1 xs

let length xs = fold (fun _ y -> 1 + y) 0 xs

let all p xs = fold (fun x y -> (p x) && y) true xs

let reverse xs = fold (fun x y -> y @ [x]) [] xs

let map f xs = fold (fun x y -> f x :: y) [] xs

let append xs ys = fold (fun x y -> x :: y) [] [xs;ys]

let any p xs = fold (fun x y -> (p x) || y) false xs 

let filter p xs = 
    let func x y =
        match (p x) with
        | true -> x::y
        | _ -> y
    fold func [] xs

1
のタイプはとfold異なるので、あなたの定義はあなたの場合です。それを明示的にするだけです。また、appendの実装が間違っています。バインドを追加したり、consを追加演算子に置き換えたりすると機能します。したがって、appendはこのリストの最良の例ではありません。List.foldList.fold('a -> 'b -> 'a) -> 'a -> 'b list -> 'a('a -> 'b -> 'b) -> 'b -> 'a list -> 'bList.collect id (fold (fun x y -> x :: y) [] [xs;ys])
jpe 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.