접기와 축소의 차이점은 무엇입니까?


121

F #을 배우려고하지만 foldreduce 를 구별하려고 할 때 혼란스러워졌습니다 . 접기는 같은 일 을하는 것처럼 보이지만 추가 매개 변수를 사용합니다. 이 두 기능이 존재하는 합법적 인 이유가 있거나 다른 배경을 가진 사람들을 수용 할 수있는 이유가 있습니까? (예 : 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])

1
reduce와 fold를 서로 fold f a l쓸 수 있습니다 reduce f a::l. 예를 들어 .
Neil

9
@Neil-의 fold관점에서 구현 하는 reduce것은 그것보다 더 복잡합니다-의 누산기 fold유형은 목록에있는 사물의 유형과 동일 할 필요가 없습니다!
Tomas Petricek

@TomasPetricek 내 실수는 원래 다른 방식으로 작성하려고했습니다.
Neil

답변:


171

Foldreduce입력 목록의 첫 번째 요소를 초기 누산기 값으로 사용하는 동안 누산기에 대한 명시적인 초기 값을 사용합니다.

즉, 누산기 및 결과 유형이 목록 요소 유형과 일치해야하지만 fold누산기가 별도로 제공되므로 다를 수 있습니다 . 이것은 유형에 반영됩니다.

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

또한 reduce빈 입력 목록에서 예외가 발생합니다.


따라서 기본적으로 수행하는 대신 fold해당 초기 값을 목록의 시작 부분에 추가하고 수행 할 수 reduce있습니까? 그렇다면 요점은 무엇입니까 fold?
Pacerier

2
@Pacerier - 배의 누적 함수는 상이한 형식을 가진다 : 'state -> 'a -> 'state대 배위한 'a -> 'a -> 'a줄이기 위해 이렇게 요소 유형과 동일한 것으로 제약을에게 결과 유형을 감소시킨다. 아래 Tomas Petricek의 답변을 참조하십시오.

178

리는 말 이외에, 당신은 정의 할 수 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)

2
런타임에 오류가 발생할 수 있도록 reduce를 정의하는 이유는 무엇입니까?
Fresheyeball

fold' & its ability to express 감소 의 일반성에 대한 메모에 +1 . 일부 언어에는 구조적 카 이랄 성 (Haskell I 'm looking at you)의 개념이 있습니다 . 이 위키 ( en.wikipedia.org/wiki/Fold_%28higher-order_function ) 에서 시각적으로 묘사 된 왼쪽 또는 오른쪽으로 접을 수 있습니다 . 식별 구조를 사용하면 다른 두 개의 '기본'FP 연산자 (필터 및 fmap)도 기존의 'fold'1 급 언어 구조 (모두 동형 구조)로 구현할 수 있습니다. ( cs.nott.ac.uk/~pszgmh/fold.pdf ) 참조 : HoTT, Princeton (이 주석 섹션은 너무 작아서 포함 할 수 없습니다 ..)
Andrew

호기심에서 .. 유형 및 예외에 대한 가정이 적기 때문에 축소의 성능이 접는 것보다 더 빠르게 만들까요?
sksallaj

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

18

fold은보다 훨씬 더 가치있는 기능 reduce입니다. 에서 다양한 기능을 정의 할 수 있습니다 fold.

reduce의 하위 집합입니다 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

1
당신은 당신의 정의 fold다르게에서 List.fold의 유형으로 List.foldIS ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a,하지만 귀하의 경우 ('a -> 'b -> 'b) -> 'b -> 'a list -> 'b. 명시 적으로하기 위해서입니다. 또한 추가 구현이 잘못되었습니다. 예를 들어 바인딩을 추가 List.collect id (fold (fun x y -> x :: y) [] [xs;ys])하거나 cons를 append 연산자로 바꾸면 작동합니다. 따라서 추가는이 목록에서 가장 좋은 예가 아닙니다.
jpe 2015
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.