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])
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.
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)
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:
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
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