Szukaj…


Wprowadzenie do foldów z garstką przykładów

Fałdy to funkcje (wyższego rzędu) używane z sekwencjami elementów. Zwijają one seq<'a> w 'b gdzie 'b jest dowolnym typem (prawdopodobnie nadal 'a ). To jest trochę abstrakcyjne, więc przejdźmy do konkretnych praktycznych przykładów.

Obliczanie sumy wszystkich liczb

W tym przykładzie 'a jest liczbą int . Mamy listę liczb i chcemy obliczyć sumę wszystkich jej liczb. Aby zsumować liczby na liście [1; 2; 3] piszemy

List.fold (fun x y -> x + y) 0 [1; 2; 3] // val it : int = 6

Pozwól mi wyjaśnić, ponieważ mamy do czynienia z listami, używamy fold w module List , stąd List.fold . pierwszym argumentem fold jest funkcja binarna, folder . Drugi argument to wartość początkowa . fold zaczyna składać listę, stosując kolejno funkcję foldera do elementów na liście, zaczynając od wartości początkowej i pierwszego elementu. Jeśli lista jest pusta, zwracana jest wartość początkowa!

Schematyczny przykład wykonania wygląda następująco:

let add x y = x + y // this is our folder (a binary function, takes two arguments)
List.fold add 0 [1; 2; 3;]
=> List.fold add (add 0 1) [2; 3] 
// the binary function is passed again as the folder in the first argument
// the initial value is updated to add 0 1 = 1 in the second argument
// the tail of the list (all elements except the first one) is passed in the third argument
// it becomes this:
List.fold add 1 [2; 3]
// Repeat untill the list is empty -> then return the "inital" value
List.fold add (add 1 2) [3]
List.fold add 3 [3] // add 1 2 = 3
List.fold add (add 3 3) [] 
List.fold add 6 [] // the list is empty now -> return 6
6

Funkcja List.sum jest z grubsza List.fold add LanguagePrimitives.GenericZero gdzie ogólne zero powoduje, że jest on zgodny z liczbami całkowitymi, liczbami List.fold add LanguagePrimitives.GenericZero , dużymi liczbami całkowitymi itp.

Zliczanie elementów na liście ( count implementacyjne)

Odbywa się to prawie tak samo jak powyżej, ale ignorując rzeczywistą wartość elementu na liście i zamiast tego dodając 1.

List.fold (fun x y -> x + 1) 0 [1; 2; 3] // val it : int = 3

Można to również zrobić w następujący sposób:

[1; 2; 3]
|> List.map (fun x -> 1) // turn every elemet into 1, [1; 2; 3] becomes [1; 1; 1]
|> List.sum // sum [1; 1; 1] is 3

Możesz więc zdefiniować count w następujący sposób:

let count xs = 
    xs 
    |> List.map (fun x -> 1) 
    |> List.fold (+) 0 // or List.sum

Znajdowanie maksimum listy

Tym razem użyjemy List.reduce który jest podobny do List.fold ale bez wartości początkowej, jak w tym przypadku, gdy nie wiemy, jaki jest typ wartości, które zestawiamy:

let max x y = if x > y then x else y
// val max : x:'a -> y: 'a -> 'a when 'a : comparison, so only for types that we can compare
List.reduce max [1; 2; 3; 4; 5] // 5
List.reduce max ["a"; "b"; "c"] // "c", because "c" > "b" > "a"
List.reduce max [true; false] // true, because true > false 

Znalezienie minimum listy

Podobnie jak przy wyszukiwaniu maksimum, folder jest inny

let min x y = if x < y then x else y
List.reduce min [1; 2; 3; 4; 5] // 1
List.reduce min ["a"; "b"; "c"] // "a"
List.reduce min [true; false] // false

Listy konkatenacyjne

Tutaj bierzemy listę list. Funkcją folderu jest operator @

// [1;2] @ [3; 4] = [1; 2; 3; 4]
let merge xs ys = xs @ ys
List.fold merge [] [[1;2;3]; [4;5;6]; [7;8;9]] // [1;2;3;4;5;6;7;8;9]

Lub możesz użyć operatorów binarnych jako funkcji folderu:

List.fold (@) [] [[1;2;3]; [4;5;6]; [7;8;9]] // [1;2;3;4;5;6;7;8;9]
List.fold (+) 0 [1; 2; 3] // 6
List.fold (||) false [true; false] // true, more on this below
List.fold (&&) true [true; false] // false, more on this below
// etc...

Obliczanie silni liczby

Taki sam pomysł jak przy sumowaniu liczb, ale teraz pomnożymy je. jeśli chcemy silnię n , mnożymy wszystkie elementy na liście [1 .. n] . Kod staje się:

// the folder
let times x y = x * y
let factorial n = List.fold times 1 [1 .. n]
// Or maybe for big integers
let factorial n = List.fold times 1I [1I .. n] 

Wdrożenie forall exists i contains

funkcja forall sprawdza, czy wszystkie elementy w sekwencji spełniają warunek. exists sprawdza, czy przynajmniej jeden element na liście spełnia warunek. Najpierw musimy wiedzieć, jak zwinąć listę wartości bool . Używamy fałd oczywiście! operatory logiczne będą naszymi funkcjami folderów.

Aby sprawdzić, czy wszystkie elementy na liście są true , zwijamy je za pomocą funkcji && z wartością true jako wartością początkową.

List.fold (&&) true [true; true; true] // true
List.fold (&&) true [] // true, empty list -> return inital value
List.fold (&&) true [false; true] // false

Podobnie, aby sprawdzić, czy jeden element jest true na boolach list, zwijamy go za pomocą || operator z wartością false jako wartość początkową:

List.fold (||) false [true; false] // true
List.fold (||) false [false; false] // false, all are false, no element is true
List.fold (||) false [] // false, empty list -> return inital value

Powrót do forall i exists . Tutaj bierzemy listę dowolnego typu, używamy warunku do przekształcenia wszystkich elementów w wartości boolowskie, a następnie zwijamy go:

let forall condition elements = 
    elements 
    |> List.map condition // condition : 'a -> bool
    |> List.fold (&&) true

let exists condition elements = 
    elements
    |> List.map condition
    |> List.fold (||) false

Aby sprawdzić, czy wszystkie elementy w [1; 2; 3; 4] są mniejsze niż 5:

forall (fun n -> n < 5) [1 .. 4] // true

zdefiniuj metodę contains z exists :

let contains x xs = exists (fun y -> y = x) xs

Lub nawet

let contains x xs = exists ((=) x) xs

Teraz sprawdź, czy lista [1 .. 5] zawiera wartość 2:

contains 2 [1..5] // true

Realizacja reverse :

let reverse xs = List.fold (fun acc x -> x :: acc) [] xs
reverse [1 .. 5] // [5; 4; 3; 2; 1]

Wdrażanie map i filter

let map f = List.fold (fun acc x -> List.append acc [f x]) List.empty
// map (fun x -> x * x) [1..5] -> [1; 4; 9; 16; 25]   

let filter p = Seq.fold (fun acc x -> seq { yield! acc; if p(x) then yield x }) Seq.empty
// filter (fun x -> x % 2 = 0) [1..10] -> [2; 4; 6; 8; 10]

Czy jest coś fold nie może zrobić? Naprawdę nie wiem

Obliczanie sumy wszystkich elementów listy

Aby obliczyć sumę terminów (typu float, int lub big integer) z listy liczb, najlepiej jest użyć List.sum. W innych przypadkach List.fold jest funkcją, która najlepiej nadaje się do obliczenia takiej sumy.

  1. Suma liczb zespolonych

W tym przykładzie deklarujemy listę liczb zespolonych i obliczamy sumę wszystkich terminów na liście.

Na początku programu dodaj odwołanie do System.Numerics

otwórz System.Numerics

Aby obliczyć sumę, inicjalizujemy akumulator do liczby zespolonej 0.

let clist = [new Complex(1.0, 52.0); new Complex(2.0, -2.0); new Complex(0.0, 1.0)]

let sum = List.fold (+) (new Complex(0.0, 0.0)) clist

Wynik:

(3, 51)
  1. Suma liczb typu unii

Załóżmy, że lista składa się z liczb typu unii (liczba zmiennoprzecinkowa lub liczba całkowita) i chcesz obliczyć sumę tych liczb.

Zadeklaruj przed następującym typem numeru:

type number = 
| Float of float
| Int of int

Oblicz sumę liczb typu numeru listy:

let list = [Float(1.3); Int(2); Float(10.2)]

let sum = List.fold (
                         fun acc elem -> 
                                        match elem with
                                        | Float(elem) -> acc + elem
                                        | Int(elem) -> acc + float(elem)
                        ) 0.0 list

Wynik:

13.5

Pierwszy parametr funkcji, która reprezentuje akumulator, jest typu float, a drugi parametr, który reprezentuje pozycję na liście, ma numer typu. Ale zanim dodamy, musimy użyć dopasowania wzorca i rzutować, by wpisać float, gdy elem jest typu Int.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow