Difference between fold and reduce?
Fold
takes an explicit initial value for the accumulator while reduce
uses the first element of the input list as the initial accumulator value.
This means the accumulator and therefore result type must match the list element type, whereas they can differ in fold
as the accumulator is provided separately. This is reflected in the types:
List.fold : ('State -> 'T -> 'State) -> 'State -> 'T list -> 'State
List.reduce : ('T -> 'T -> 'T) -> 'T list -> 'T
In addition reduce
throws an exception on an empty input list.
fold
is a much more valuable function than reduce
. You can define many different functions in terms of fold
.
reduce
is just a subset of fold
.
Definition of fold:
let rec fold f v xs =
match xs with
| [] -> v
| (x::xs) -> f (x) (fold f v xs )
Examples of functions defined in terms of 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
In addition to what Lee said, you can define reduce
in terms of fold
, but not (easily) the other way round:
let reduce f list =
match list with
| head::tail -> List.fold f head tail
| [] -> failwith "The list was empty!"
The fact that fold
takes an explicit initial value for the accumulator also means that the result of the fold
function can have a different type than the type of values in the list. For example, you can use accumulator of type string
to concatenate all numbers in a list into a textual representation:
[1 .. 10] |> List.fold (fun str n -> str + "," + (string n)) ""
When using reduce
, the type of accumulator is the same as the type of values in the list - this means that if you have a list of numbers, the result will have to be a number. To implement the previous sample, you'd have to convert the numbers to string
first and then accumulate:
[1 .. 10] |> List.map string
|> List.reduce (fun s1 s2 -> s1 + "," + s2)