Katla ve azalt arasındaki fark?


121

F # öğrenmeye çalışıyorum ama katla ve indirgemeyi ayırt etmeye çalışırken kafam karıştı . Fold aynı şeyi yapıyor gibi görünüyor ancak fazladan bir parametre alıyor. Bu iki işlevin var olmasının meşru bir nedeni var mı yoksa farklı geçmişlere sahip insanları barındırmak için var mı? (Örneğin: C # 'da dize ve dize)

Örnekten kopyalanan kod parçacığı:

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
Birbirleri açısından küçültme ve katlama yazabilirsiniz, örneğin fold f a lolarak yazılabilir reduce f a::l.
Neil

9
@Neil - foldaçısından uygulama yapmak reducebundan daha karmaşıktır - biriktiricinin foldtürü listedeki şeylerin türüyle aynı olmak zorunda değildir!
Tomas Petricek

@TomasPetricek Benim hatam, aslında bunu tam tersi şekilde yazmak istedim.
Neil

Yanıtlar:


171

Foldakümülatör için açık bir başlangıç ​​değeri alırken reduce, giriş listesinin ilk öğesini ilk akümülatör değeri olarak kullanır.

Bu, toplayıcının ve dolayısıyla sonuç türünün liste öğesi türüyle eşleşmesi gerektiği anlamına gelir, ancak foldtoplayıcı ayrı olarak sağlandıkça bunlar farklılık gösterebilir . Bu türlere yansır:

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

Ek reduceolarak, boş bir giriş listesine bir istisna atar.


Yani temelde yapmak yerine, foldbu başlangıç ​​değerini listenin başına ekleyebilir ve yapabilir reducemisiniz? O foldzaman ne anlamı var ?
Pacerier

2
@Pacerier - Katlama için biriktirici işlevi farklı bir türe sahiptir: 'state -> 'a -> 'statekatlama 'a -> 'a -> 'aiçin azaltma için, bu nedenle azalt, sonuç türünü öğe türüyle aynı olacak şekilde kısıtlar. Aşağıdaki Tomas Petricek'in cevabına bakın.
Lee

178

Lee'nin söylediklerine ek olarak reduce, terimleriyle de tanımlayabilirsiniz fold, ancak tersi (kolayca) yapamazsınız:

let reduce f list = 
  match list with
  | head::tail -> List.fold f head tail
  | [] -> failwith "The list was empty!"

foldToplayıcı için açık bir başlangıç ​​değeri alması gerçeği aynı zamanda foldişlevin sonucunun listedeki değerlerin türünden farklı bir türe sahip olabileceği anlamına gelir . Örneğin, stringbir listedeki tüm sayıları metinsel bir temsilde birleştirmek için bir biriktirici türü kullanabilirsiniz :

[1 .. 10] |> List.fold (fun str n -> str + "," + (string n)) ""

Kullanırken reduce, akümülatör türü listedeki değerlerin türüyle aynıdır - bu, bir sayı listeniz varsa, sonucun bir sayı olması gerektiği anlamına gelir. Önceki örneği uygulamak için önce sayıları dönüştürmeniz stringve ardından biriktirmeniz gerekir:

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

2
Neden çalışma zamanında hata verebileceği şekilde azaltmayı tanımlayın?
Fresheyeball

İndirgeme genelliği üzerine not için +1 fold' & its ability to express '. Bazı dillerin yapısal kiralite kavramı vardır (Haskell, sana bakıyorum) bu wiki'de görsel olarak resmedilen ( en.wikipedia.org/wiki/Fold_%28higher-order_function ) sola veya sağa katlanabilirsin . Bir kimlik yapısı ile, diğer iki 'temel' FP operatörü (filtre ve fmap) mevcut bir birinci sınıf dil yapısı ile de uygulanabilir (hepsi eşbiçimli yapılardır). ( cs.nott.ac.uk/~pszgmh/fold.pdf ) Bakınız: HoTT, Princeton (Bu yorum bölümü içeremeyecek kadar küçük ..)
Andrew

Meraktan ... bu, türler ve istisnalar hakkında daha az varsayım altında olduğu için düşüşün performansını katlamadan daha hızlı yapar mı?
sksallaj

19

İmzalarına bakalım:

> 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>

Bazı önemli farklılıklar var:

  • reduceYalnızca bir tür öğe üzerinde çalışırken , içindeki biriktirici ve liste öğeleri foldfarklı türlerde olabilir.
  • İle reduce, filkinden başlayarak her liste öğesine bir işlev uygularsınız :

    f (... (f i0 i1) i2 ...) iN.

    İle akümülatörden başlayarak folduygularsınız :fs

    f (... (f s i0) i1 ...) iN.

Bu nedenle, boş bir liste ile reducesonuçlanır ArgumentException. Dahası, foldşundan daha geneldir reduce; kolayca folduygulamak için kullanabilirsiniz reduce.

Bazı durumlarda kullanmak reducedaha kısa ve özdür:

// Return the last element in the list
let last xs = List.reduce (fun _ x -> x) xs

veya makul bir akümülatör yoksa daha kullanışlıdır:

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

Genel olarak, foldrastgele tipte bir akümülatör ile daha güçlüdür:

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

18

foldçok daha değerli bir işlevdir reduce. Açısından birçok farklı fonksiyon tanımlayabilirsiniz fold.

reducesadece bir alt kümesidir fold.

Kıvrımın tanımı:

let rec fold f v xs =
    match xs with 
    | [] -> v
    | (x::xs) -> f (x) (fold f v xs )

Katlama açısından tanımlanan işlevlere örnekler:

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
Eğer tanımlayan foldfarklı dan List.foldtürü gibi List.foldDİR ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a, ancak davanızı ('a -> 'b -> 'b) -> 'b -> 'a list -> 'b. Açıkça söylemek için. Ayrıca, append uygulamanız da yanlış. Buna bir bağlama eklerseniz, örneğin List.collect id (fold (fun x y -> x :: y) [] [xs;ys]), eksilerini ekleme operatörü ile değiştirirseniz işe yarar . Bu nedenle append bu listedeki en iyi örnek değildir.
jpe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.