130

F# を学習しようとしていますが、 foldreduceを区別しようとして混乱しました。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])
4

4 に答える 4

182

リーが言ったことに加えてreduce、 の観点から定義することはできますfoldが、(簡単に) その逆はできません。

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

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

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

を使用する場合reduce、accumulator のタイプはリスト内の値のタイプと同じです。これは、数値のリストがある場合、結果は数値でなければならないことを意味します。前のサンプルを実装するには、数値をstring最初に変換してから累積する必要があります。

[1 .. 10] |> List.map string
          |> List.reduce (fun s1 s2 -> s1 + "," + s2)
于 2012-01-29T19:14:01.503 に答える
178

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

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

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

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

于 2012-01-29T19:08:39.650 に答える
21

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

reduceの単なるサブセットですfold

折り目の定義:

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

フォールドに関して定義された関数の例:

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
于 2014-03-15T04:46:06.420 に答える
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>

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

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

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

    では、アキュムレータから開始してfold適用します。fs

    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
于 2012-01-29T19:25:17.483 に答える