Zoeken…


Inleiding tot vouwen, met een handvol voorbeelden

Vouwen zijn (hogere volgorde) functies die worden gebruikt met reeksen elementen. Ze vouwen seq<'a> in 'b waarbij 'b elk type is (mogelijk nog 'a ). Dit is een beetje abstract, dus laten we in concrete praktische voorbeelden komen.

De som van alle getallen berekenen

In dit voorbeeld is 'a een int . We hebben een lijst met getallen en we willen alle getallen optellen. Om de nummers van de lijst op te tellen [1; 2; 3] we schrijven

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

Laat het me uitleggen, omdat we te maken hebben met lijsten, gebruiken we fold in de List module, vandaar List.fold . het eerste argument dat fold is een binaire functie, de map . Het tweede argument is de beginwaarde . fold begint met het vouwen van de lijst door de mapfunctie opeenvolgend toe te passen op elementen in de lijst die beginnen met de beginwaarde en het eerste element. Als de lijst leeg is, wordt de beginwaarde geretourneerd!

Schematisch overzicht van een uitvoeringsvoorbeeld ziet er als volgt uit:

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

De functie List.sum is grofweg List.fold add LanguagePrimitives.GenericZero waarbij de generieke nul het compatibel maakt met gehele getallen, drijvers, grote gehele getallen enz.

Elemets in een lijst count ( count )

Dit gebeurt bijna hetzelfde als hierboven, maar door de werkelijke waarde van het element in de lijst te negeren en in plaats daarvan 1 toe te voegen.

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

Dit kan ook als volgt worden gedaan:

[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

Dus je zou het count als volgt kunnen definiëren:

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

Het maximum van de lijst vinden

Deze keer gebruiken we List.reduce die lijkt op List.fold maar zonder een beginwaarde, zoals in dit geval waarin we niet weten wat het type is van de waarden die we vergelijken:

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 

Het minimum van een lijst vinden

Net zoals bij het vinden van het maximum, is de map anders

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

Lijsten samenvoegen

Hier nemen we een lijst met lijsten. De mapfunctie is de 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]

Of u kunt binaire operatoren gebruiken als uw mapfunctie:

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

De faculteit van een getal berekenen

Hetzelfde idee als bij het optellen van de getallen, maar nu vermenigvuldigen we ze. als we de faculteit van n willen, vermenigvuldigen we alle elementen in de lijst [1 .. n] . Code wordt:

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

forall , exists en contains

de functie forall controleert of alle elementen in een reeks aan een voorwaarde voldoen. exists controleert of ten minste één element in de lijst aan de voorwaarde voldoet. Eerst moeten we weten hoe we een lijst met bool waarden kunnen samenvouwen. Nou, we gebruiken natuurlijk vouwen! Booleaanse operatoren zullen onze mapfuncties zijn.

Om te controleren of alle elementen in een lijst true , vouwen we ze samen met de functie && met true als beginwaarde.

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

Evenzo, om te controleren of een element true in een lijst Booleans, vouwen we het samen met de || operator met false als beginwaarde:

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

Terug naar forall en exists . Hier nemen we een lijst van elk type, gebruiken we de voorwaarde om alle elementen om te zetten in Booleaanse waarden en vouwen we deze samen:

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

Om te controleren of alle elementen in [1; 2; 3; 4] zijn kleiner dan 5:

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

definieer de methode contains met exists :

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

Of zelfs

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

Controleer nu of de lijst [1 .. 5] de waarde 2 bevat:

contains 2 [1..5] // true

reverse implementeren:

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

map en filter implementeren

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]

Is er iets dat fold niet kan doen? Ik weet het niet echt

De som van alle elementen van een lijst berekenen

Om de som van termen (van het type float, int of groot geheel getal) van een nummerlijst te berekenen, verdient het de voorkeur om List.sum te gebruiken. In andere gevallen is List.fold de functie die het meest geschikt is om een dergelijke som te berekenen.

  1. Som van complexe getallen

In dit voorbeeld declareren we een lijst met complexe getallen en berekenen we de som van alle termen in de lijst.

Voeg aan het begin van het programma een verwijzing naar System.Numerics toe

open System.Numerics

Om de som te berekenen, initialiseren we de accumulator op het complexe getal 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

Resultaat:

(3, 51)
  1. Som van aantallen van unietype

Stel dat een lijst bestaat uit getallen van het type unie (float of int) en de som van deze getallen wilt berekenen.

Verklaar vóór het volgende nummertype:

type number = 
| Float of float
| Int of int

Bereken de som van getallen van typenummer van een lijst:

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

Resultaat:

13.5

De eerste parameter van de functie, die de accumulator vertegenwoordigt, is van het type float en de tweede parameter, die een item in de lijst vertegenwoordigt, is van het typenummer. Maar voordat we toevoegen, moeten we een patroonovereenkomst gebruiken en casten om float te typen wanneer elem van het type Int is.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow