尝试学习F#但在尝试区分fold和reduce时感到困惑。折叠似乎是same thing,但需要额外的参数。是否存在这两种功能存在的合理原因,或者它们是否适合具有不同背景的人? (例如: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])
答案 0 :(得分:177)
除了Lee所说的,你可以用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)
答案 1 :(得分:159)
Fold
为累加器采用显式初始值,而reduce
使用输入列表的第一个元素作为初始累加器值。
这意味着累加器和结果类型必须与列表元素类型匹配,而它们可以在fold
中不同,因为累加器是单独提供的。这反映在以下类型中:
List.fold : ('State -> 'T -> 'State) -> 'State -> 'T list -> 'State
List.reduce : ('T -> 'T -> 'T) -> 'T list -> 'T
此外,reduce
会在空输入列表上抛出异常。
答案 2 :(得分: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
中的累加器和列表元素可以使用不同的类型。使用reduce
,您可以将函数f
应用于从第一个列表元素开始的每个列表元素:
f (... (f i0 i1) i2 ...) iN
。
使用fold
,您可以从累加器f
开始应用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
答案 3 :(得分:15)
fold
比reduce
更有价值。您可以根据fold
定义许多不同的功能。
reduce
只是fold
的一部分。
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