web-dev-qa-db-fra.com

Différence entre plier et réduire?

J'essaie d'apprendre F # mais je suis devenu confus en essayant de faire la distinction entre fold et réduire . Fold semble faire le même chose mais prend un paramètre supplémentaire. Y a-t-il une raison légitime à l'existence de ces deux fonctions ou sont-elles là pour accueillir des personnes d'horizons différents? (Par exemple: chaîne et chaîne en C #)

Voici l'extrait de code copié à partir de l'exemple:

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])
115
Wallace

Fold prend une valeur initiale explicite pour l'accumulateur tandis que reduce utilise le premier élément de la liste d'entrée comme valeur initiale de l'accumulateur.

Cela signifie que l'accumulateur et donc le type de résultat doivent correspondre au type d'élément de liste, alors qu'ils peuvent différer dans fold car l'accumulateur est fourni séparément. Cela se reflète dans les types:

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

De plus, reduce lève une exception sur une liste d'entrée vide.

161
Lee

En plus de ce que Lee a dit, vous pouvez définir reduce en termes de fold, mais pas (facilement) l'inverse:

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

Le fait que fold prend une valeur initiale explicite pour l'accumulateur signifie également que le résultat de la fonction fold peut avoir un type différent de celui des valeurs de la liste. Par exemple, vous pouvez utiliser un accumulateur de type string pour concaténer tous les nombres d'une liste en une représentation textuelle:

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

Lorsque vous utilisez reduce, le type d'accumulateur est le même que le type de valeurs dans la liste - cela signifie que si vous avez une liste de nombres, le résultat devra être un nombre. Pour implémenter l'exemple précédent, vous devez d'abord convertir les nombres en string, puis accumuler:

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

Regardons leurs signatures:

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

Il existe quelques différences importantes:

  • Alors que reduce ne fonctionne que sur un seul type d’éléments, les éléments accumulateur et liste dans fold peuvent être de types différents.
  • Avec reduce, vous appliquez une fonction f à chaque élément de la liste à partir du premier:

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

    Avec fold, vous appliquez f à partir de l'accumulateur s:

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

Par conséquent, reduce entraîne un ArgumentException dans une liste vide. De plus, fold est plus générique que reduce; vous pouvez utiliser fold pour implémenter facilement reduce.

Dans certains cas, l'utilisation de reduce est plus succincte:

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

ou plus pratique s'il n'y a pas d'accumulateur raisonnable:

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

En général, fold est plus puissant avec un accumulateur de type arbitraire:

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

fold est une fonction beaucoup plus précieuse que reduce. Vous pouvez définir de nombreuses fonctions différentes en termes de fold.

reduce n'est qu'un sous-ensemble de fold.

Définition du pli:

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

Exemples de fonctions définies en termes de pli:

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
16
Raz Megrelidze