折叠与减少之间的区别?

Wal*_*ace 115 reduce f# functional-programming fold

试图学习F#,但在尝试区分折叠减少时感到困惑.折叠似乎做同样的事情,但需要额外的参数.是否存在这两种功能存在的合理原因,或者它们是否适合具有不同背景的人?(例如: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])
Run Code Online (Sandbox Code Playgroud)

Tom*_*cek 178

除了什么李先生说,你可以定义reduce来讲fold,而不是(容易)倒过来:

let reduce f list = 
  match list with
  | head::tail -> List.fold f head tail
  | [] -> failwith "The list was empty!"
Run Code Online (Sandbox Code Playgroud)

fold对累加器采用显式初始值的事实也意味着fold函数的结果可以具有与列表中的值类型不同的类型.例如,您可以使用类型的累加器string将列表中的所有数字连接成文本表示:

[1 .. 10] |> List.fold (fun str n -> str + "," + (string n)) ""
Run Code Online (Sandbox Code Playgroud)

使用时reduce,累加器的类型与列表中的值类型相同 - 这意味着如果您有数字列表,则结果必须是数字.要实现上一个示例,您必须string先将数字转换为第一个然后累积:

[1 .. 10] |> List.map string
          |> List.reduce (fun s1 s2 -> s1 + "," + s2)
Run Code Online (Sandbox Code Playgroud)

  • 为什么定义 reduce 使其在运行时可能出错? (2认同)

Lee*_*Lee 164

Foldreduce使用输入列表的第一个元素作为初始累加器值时,为累加器采用显式初始值.

这意味着累加器和结果类型必须与列表元素类型匹配,而它们可以不同,fold因为累加器是单独提供的.这反映在以下类型中:

List.fold : ('State -> 'T -> 'State) -> 'State -> 'T list -> 'State
List.reduce : ('T -> 'T -> 'T) -> 'T list -> 'T
Run Code Online (Sandbox Code Playgroud)

另外reduce在空输入列表上抛出异常.

  • @Pacerier - fold的累加器函数有不同的类型:`'state - >'a - >'state` for fold vs`'a - >'a - >'a` for reduce,所以reduce将结果类型约束为与元素类型相同.请参阅下面的[Tomas Petricek的回答](http://stackoverflow.com/a/9055928/152602). (2认同)

pad*_*pad 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>
Run Code Online (Sandbox Code Playgroud)

有一些重要的区别:

  • 虽然仅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
Run Code Online (Sandbox Code Playgroud)

如果没有合理的累加器,或者更方便:

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

通常,fold使用任意类型的累加器更强大:

// Reverse a list using an empty list as the accumulator
let rev xs = List.fold (fun acc x -> x::acc) [] xs
Run Code Online (Sandbox Code Playgroud)


Raz*_*dze 16

fold是一个比它更有价值的功能reduce.您可以根据需要定义许多不同的功能fold.

reduce只是一个子集fold.

折叠的定义:

let rec fold f v xs =
    match xs with 
    | [] -> v
    | (x::xs) -> f (x) (fold f v xs )
Run Code Online (Sandbox Code Playgroud)

以折叠方式定义的函数示例:

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
Run Code Online (Sandbox Code Playgroud)