Listen

Eine Liste in F# stellt eine geordnete, unveränderliche Reihe von Elementen desselben Typs dar. Verwenden Sie die Funktionen im Listenmodul, um grundlegende Operationen mit Listen durchzuführen.

Erstellen und Initialisieren von Listen

Sie können eine Liste definieren, indem Sie die durch Semikolon getrennten und in eckige Klammern eingeschlossenen Elemente explizit auflisten, wie in folgender Codezeile veranschaulicht.

let list123 = [ 1; 2; 3 ]

Sie können Elemente auch durch Zeilenumbrüche trennen, wobei die Semikolons dann optional sind. Letztere Syntax kann den Code übersichtlicher gestalten, wenn die Ausdrücke zur Elementinitialisierung umfangreicher ausfallen oder Sie für jedes Element einen Kommentar einbeziehen möchten.

let list123 = [ 1; 2; 3 ]

Im Allgemeinen müssen alle Listenelemente denselben Typ aufweisen. Eine Ausnahme ist, dass eine Liste, in der für Elemente ein Basistyp angegeben wird, Elemente enthalten darf, die abgeleiteten Typen entsprechen. Somit ist Folgendes zulässig, da sowohl Button und CheckBox von Control abgeleitet werden.

let myControlList: Control list = [ new Button(); new CheckBox() ]

Sie können Listenelemente auch durch Verwendung eines Bereichs definieren, der durch ganze Zahlen angegeben wird, die durch den Bereichsoperator (..) getrennt sind, wie im folgenden Code veranschaulicht.

let list1 = [ 1..10 ]

Eine leere Liste wird durch ein Paar eckige Klammern angegeben, die nichts einschließen.

// An empty list.
let listEmpty = []

Sie können eine Liste auch mithilfe von Sequenzausdrücken erstellen. Weitere Informationen finden Sie unter Sequenzausdrücke. Der folgende Code erstellt z. B. eine Liste mit den Quadratzahlen der ganzen Zahlen von 1 bis 10.

let listOfSquares = [ for i in 1..10 -> i * i ]

Operatoren für die Arbeit mit Listen

Sie können Elemente mithilfe des Operators :: (Cons) zu einer Liste hinzufügen. Wenn list1 den Werten [2; 3; 4] entspricht, erstellt der folgende Code für list2 die Werte [100; 2; 3; 4].

let list2 = 100 :: list1

Sie können Listen, die kompatible Typen aufweisen, wie im folgenden Code mithilfe des Operators @ verketten. Wenn list1 den Werten [2; 3; 4] und list2 den Werten [100; 2; 3; 4] entspricht, erstellt dieser Code für list3 die Werte [2; 3; 4; 100; 2; 3; 4].

let list3 = list1 @ list2

Funktionen, die Operationen mit Listen ausführen, stehen im Listenmodulzur Verfügung.

Da Listen in F# unveränderlich sind, erstellen alle modifizierenden Operationen neue Listen, anstatt die vorhandenen Listen zu verändern.

Listen werden in F# als einfach verknüpfte Listen implementiert. Das bedeutet, dass Operationen, die nur auf den Anfang der Liste zugreifen, O(1) entsprechen, während der Elementzugriff durch O(n) realisiert wird.

Eigenschaften

Der Listentyp unterstützt die folgenden Eigenschaften:

Eigenschaft Typ Beschreibung
Head 'T Das erste Element.
Leer 'T list Eine statische Eigenschaft, die eine leere Liste des entsprechenden Typs zurückgibt.
IsEmpty bool Ergibt true, wenn die Liste keine Elemente enthält.
Element 'T Das Element am angegebenen (nullbasierten) Index.
Länge int Die Anzahl der Elemente.
Tail 'T list Die Liste ohne das erste Element.

Nachfolgend sind einige Beispiele zur Verwendung dieser Eigenschaften aufgeführt.

let list1 = [ 1; 2; 3 ]

// Properties
printfn "list1.IsEmpty is %b" (list1.IsEmpty)
printfn "list1.Length is %d" (list1.Length)
printfn "list1.Head is %d" (list1.Head)
printfn "list1.Tail.Head is %d" (list1.Tail.Head)
printfn "list1.Tail.Tail.Head is %d" (list1.Tail.Tail.Head)
printfn "list1.Item(1) is %d" (list1.Item(1))

Verwenden von Listen

Durch die Programmierung mit Listen können Sie mit wenig Code komplexe Operationen durchführen. In diesem Abschnitt werden die allgemeinen Operationen für Listen beschrieben, die für die funktionale Programmierung von Bedeutung sind.

Rekursion mit Listen

Listen sind beispiellos für rekursive Programmierverfahren geeignet. Betrachten Sie eine Operation, die für jedes Element einer Liste ausgeführt werden muss. Sie können dazu rekursiv vorgehen, indem Sie den Listenanfang verarbeiten und dann den Rest der Liste, bei dem es sich um eine kleinere Liste handelt, die aus der ursprünglichen Liste ohne das erste Element besteht, zurück an die nächste Rekursionsstufe übergeben.

Sie verwenden zum Schreiben einer derartigen rekursiven Funktion den Cons-Operator (::) für den Mustervergleich, der es Ihnen ermöglicht, den Listenanfang vom Rest zu trennen.

Das folgende Codebeispiel zeigt, wie eine rekursive Funktion mithilfe des Musterabgleichs implementiert wird, die Operationen mit einer Liste ausführt.

let rec sum list =
    match list with
    | head :: tail -> head + sum tail
    | [] -> 0

Der vorherige Code ist für kleine Listen geeignet, aber bei größeren Listen könnten Stapelüberläufe auftreten. Der folgende Code verwendet ein Akkumulatorargument, ein Standardverfahren für die Arbeit mit rekursiven Funktionen, um den vorherigen Code zu verbessern. Durch die Verwendung des Akkumulatorarguments wird die Funktion zur endrekursiven Funktion, wodurch Stapelspeicher gespart wird.

let sum list =
    let rec loop list acc =
        match list with
        | head :: tail -> loop tail (acc + head)
        | [] -> acc

    loop list 0

Die Funktion RemoveAllMultiples ist eine rekursive Funktion, die zwei Listen übernimmt. Die erste Liste enthält die Zahlen, deren Vielfaches entfernt wird. Die zweite Liste ist die Liste, aus der die Zahlen entfernt werden. Der Code im folgenden Beispiel verwendet diese rekursive Funktion, um alle Nichtprimzahlen aus der Liste zu entfernen, sodass als Ergebnis eine Liste der Primzahlen verbleibt.

let IsPrimeMultipleTest n x = x = n || x % n <> 0

let rec RemoveAllMultiples listn listx =
    match listn with
    | head :: tail -> RemoveAllMultiples tail (List.filter (IsPrimeMultipleTest head) listx)
    | [] -> listx


let GetPrimesUpTo n =
    let max = int (sqrt (float n))
    RemoveAllMultiples [ 2..max ] [ 1..n ]

printfn "Primes Up To %d:\n %A" 100 (GetPrimesUpTo 100)

Die Ausgabe lautet wie folgt:

Primes Up To 100:
[2; 3; 5; 7; 11; 13; 17; 19; 23; 29; 31; 37; 41; 43; 47; 53; 59; 61; 67; 71; 73; 79; 83; 89; 97]

Modulfunktionen

Das Listenmodul stellt Funktionen bereit, die auf die Elemente einer Liste zugreifen. Auf das Anfangselement kann am schnellsten und einfachsten zugegriffen werden. Verwenden Sie dazu die Head-Eigenschaft oder die List.head-Modulfunktion. Mithilfe der Tail-Eigenschaft oder der List.tail-Funktion können Sie auf den Rest einer Liste zugreifen. Verwenden Sie die List.nth-Funktion, um ein Element über den Index zu finden. Mithilfe von List.nth wird die Liste durchlaufen. Daher handelt es sich um O(n). Wenn in Ihrem Code häufig List.nth verwendet wird, können Sie erwägen, ob Sie anstelle der Liste ein Array verwenden möchten. Der Elementzugriff in Arrays entspricht "O(1)".

Boolesche Operationen für Listen

Die List.isEmpty-Funktion ermittelt, ob eine Liste Elemente enthält.

Die List.exists-Funktion wendet einen booleschen Test auf Elemente einer Liste an und gibt true zurück, wenn ein Element den Test erfüllt. List.exists2 verhält sich ähnlich, aber arbeitet in zwei Listen mit aufeinander folgenden Elementpaaren.

Das folgende Codebeispiel veranschaulicht die Verwendung von List.exists.

// Use List.exists to determine whether there is an element of a list satisfies a given Boolean expression.
// containsNumber returns true if any of the elements of the supplied list match
// the supplied number.
let containsNumber number list = List.exists (fun elem -> elem = number) list
let list0to3 = [0 .. 3]
printfn "For list %A, contains zero is %b" list0to3 (containsNumber 0 list0to3)

Die Ausgabe lautet wie folgt:

For list [0; 1; 2; 3], contains zero is true

Das folgende Beispiel veranschaulicht die Verwendung von List.exists2.

// Use List.exists2 to compare elements in two lists.
// isEqualElement returns true if any elements at the same position in two supplied
// lists match.
let isEqualElement list1 list2 = List.exists2 (fun elem1 elem2 -> elem1 = elem2) list1 list2
let list1to5 = [ 1 .. 5 ]
let list5to1 = [ 5 .. -1 .. 1 ]
if (isEqualElement list1to5 list5to1) then
    printfn "Lists %A and %A have at least one equal element at the same position." list1to5 list5to1
else
    printfn "Lists %A and %A do not have an equal element at the same position." list1to5 list5to1

Die Ausgabe lautet wie folgt:

Lists [1; 2; 3; 4; 5] and [5; 4; 3; 2; 1] have at least one equal element at the same position.

Sie können List.forall verwenden, wenn Sie testen möchten, ob alle Elemente in einer Liste eine Bedingung erfüllen.

let isAllZeroes list = List.forall (fun elem -> elem = 0.0) list
printfn "%b" (isAllZeroes [0.0; 0.0])
printfn "%b" (isAllZeroes [0.0; 1.0])

Die Ausgabe lautet wie folgt:

true
false

Entsprechend ermittelt List.forall2, ob alle Elemente an den entsprechenden Positionen in zwei Listen einen booleschen Ausdruck erfüllen, der jedes Elementpaar einbezieht.

let listEqual list1 list2 = List.forall2 (fun elem1 elem2 -> elem1 = elem2) list1 list2
printfn "%b" (listEqual [0; 1; 2] [0; 1; 2])
printfn "%b" (listEqual [0; 0; 0] [0; 1; 0])

Die Ausgabe lautet wie folgt:

true
false

Sortieroperationen für Listen

Die Funktionen List.sort, List.sortBy und List.sortWith sortieren Listen. Die Sortierfunktion bestimmt, welche dieser drei Funktionen verwendet wird. List.sort verwendet einen generischen Standardvergleich. Beim generischen Vergleich werden globale Operatoren, die auf der allgemeinen Vergleichsfunktion basieren, zum Vergleichen von Werten verwendet. Der generische Vergleich funktioniert mit einer Vielzahl von Elementtypen sehr effizient, z. B. mit einfachen numerischen Typen, Tupeln, Datensätzen, diskriminierten Unions, Listen, Arrays und allen anderen Typen, die System.IComparable implementieren. Für Typen, die System.IComparable implementieren, wird für den generischen Vergleich die Funktion System.IComparable.CompareTo() verwendet. Der generische Vergleich funktioniert auch mit Zeichenfolgen, wobei jedoch eine kulturunabhängige Sortierreihenfolge verwendet wird. Der generische Vergleich sollte nicht für Typen verwendet werden, die nicht unterstützt werden, z. B. für Funktionstypen. Die optimale Leistung erzielt der generische Standardvergleich außerdem für kleine strukturierte Typen. Für größere strukturierte Typen, die häufig verglichen und sortiert werden müssen, sollten Sie die Implementierung von System.IComparable erwägen und eine effiziente Implementierung der Methode System.IComparable.CompareTo() bereitstellen.

List.sortBy übernimmt eine Funktion, die einen Wert zurückgibt, der als Sortierkriterium verwendet wird, während List.sortWith eine Vergleichsfunktion als Argument übernimmt. Die letzten beiden Funktionen sind hilfreich, wenn Sie mit Typen arbeiten, die keinen Vergleich unterstützen bzw. wenn der Vergleich eine komplexere Vergleichssemantik als bei kulturfähige Zeichenfolgen erfordert.

Das folgende Beispiel veranschaulicht die Verwendung von List.sort.

let sortedList1 = List.sort [1; 4; 8; -2; 5]
printfn "%A" sortedList1

Die Ausgabe lautet wie folgt:

[-2; 1; 4; 5; 8]

Das folgende Beispiel veranschaulicht die Verwendung von List.sortBy.

let sortedList2 = List.sortBy (fun elem -> abs elem) [1; 4; 8; -2; 5]
printfn "%A" sortedList2

Die Ausgabe lautet wie folgt:

[1; -2; 4; 5; 8]

Das nächste Beispiel veranschaulicht die Verwendung von List.sortWith. In diesem Beispiel wird die benutzerdefinierte Vergleichsfunktion compareWidgets zunächst dazu verwendet, um ein Feld mit benutzerdefiniertem Typ zu vergleichen. Anschließend wird ein weiteres Feld verglichen, wenn die Werte des ersten Felds gleich sind.

type Widget = { ID: int; Rev: int }

let compareWidgets widget1 widget2 =
   if widget1.ID < widget2.ID then -1 else
   if widget1.ID > widget2.ID then 1 else
   if widget1.Rev < widget2.Rev then -1 else
   if widget1.Rev > widget2.Rev then 1 else
   0

let listToCompare = [
    { ID = 92; Rev = 1 }
    { ID = 110; Rev = 1 }
    { ID = 100; Rev = 5 }
    { ID = 100; Rev = 2 }
    { ID = 92; Rev = 1 }
    ]

let sortedWidgetList = List.sortWith compareWidgets listToCompare
printfn "%A" sortedWidgetList

Die Ausgabe lautet wie folgt:

[{ID = 92;
Rev = 1;}; {ID = 92;
Rev = 1;}; {ID = 100;
Rev = 2;}; {ID = 100;
Rev = 5;}; {ID = 110;
Rev = 1;}]

Suchoperationen für Listen

Für Listen werden zahlreiche Suchoperationen unterstützt. Mithilfe der einfachsten Operation, List.find, können Sie das erste Element finden, das mit einer angegebenen Bedingung übereinstimmt.

Das folgende Codebeispiel veranschaulicht die Verwendung von List.find anhand der Suche nach der ersten Zahl in einer Liste, die durch fünf geteilt werden kann.

let isDivisibleBy number elem = elem % number = 0
let result = List.find (isDivisibleBy 5) [ 1 .. 100 ]
printfn "%d " result

The result is 5.

Wenn die Elemente zunächst transformiert werden müssen, rufen Sie List.pick auf, das eine Funktion übernimmt, die nach dem ersten Optionswert sucht, der Some(x) entspricht. Anstatt ein Element zurückzugeben, gibt List.pick das Ergebnis x zurück. Wenn kein übereinstimmendes Element gefunden wird, löst List.pick die Ausnahme System.Collections.Generic.KeyNotFoundException aus. Im folgenden Code wird die Verwendung von List.pick veranschaulicht:

let valuesList = [ ("a", 1); ("b", 2); ("c", 3) ]

let resultPick = List.pick (fun elem ->
                    match elem with
                    | (value, 2) -> Some value
                    | _ -> None) valuesList
printfn "%A" resultPick

Die Ausgabe lautet wie folgt:

"b"

Eine weitere Gruppe von Suchoperationen, List.tryFind und zugehörige Funktionen, geben einen Optionswert zurück. Die List.tryFind-Funktion gibt das erste Element einer Liste zurück, das eine Bedingung erfüllt, sofern dieses Element vorhanden ist. Wenn dies nicht der Fall ist, wird der Optionswert None zurückgegeben. Die Variation List.tryFindIndex gibt anstelle des Elements, sofern eines gefunden wird, den Index des Elements zurück. Diese Funktionen werden im folgenden Code veranschaulicht.

let list1d = [1; 3; 7; 9; 11; 13; 15; 19; 22; 29; 36]
let isEven x = x % 2 = 0
match List.tryFind isEven list1d with
| Some value -> printfn "The first even value is %d." value
| None -> printfn "There is no even value in the list."

match List.tryFindIndex isEven list1d with
| Some value -> printfn "The first even value is at position %d." value
| None -> printfn "There is no even value in the list."

Die Ausgabe lautet wie folgt:

The first even value is 22.
The first even value is at position 8.

Arithmetische Operationen für Listen

Allgemeine arithmetische Operationen wie für Summe und Mittelwert sind in das Listenmodul integriert. Damit er für List.sum geeignet ist, muss der Listenelementtyp den +-Operator unterstützen und einen Nullwert besitzen. Alle integrierten arithmetischen Typen erfüllen diese Bedingungen. Damit er für List.average geeignet ist, muss der Elementtyp die Division ohne Rest unterstützen, wodurch ganzzahlige Typen ausgeschlossen, aber Gleitkommatypen zulässig sind. Die Funktionen List.sumBy und List.averageBy verwenden eine Funktion als Parameter, und die Ergebnisse dieser Funktion werden zum Berechnen der Werte für die Summe oder den Mittelwert verwendet.

Das folgende Codebeispiel veranschaulicht die Verwendung von List.sum, List.sumBy und List.average.

// Compute the sum of the first 10 integers by using List.sum.
let sum1 = List.sum [1 .. 10]

// Compute the sum of the squares of the elements of a list by using List.sumBy.
let sum2 = List.sumBy (fun elem -> elem*elem) [1 .. 10]

// Compute the average of the elements of a list by using List.average.
let avg1 = List.average [0.0; 1.0; 1.0; 2.0]

printfn "%f" avg1

Die Ausgabe lautet 1.000000.

Im folgenden Code wird die Verwendung von List.averageBy veranschaulicht:

let avg2 = List.averageBy (fun elem -> float elem) [1 .. 10]
printfn "%f" avg2

Die Ausgabe lautet 5.5.

Listen und Tupel

Listen, die Tupel enthalten, können mithilfe von Funktionen zum Zippen und Entzippen verändert werden. Diese Funktionen vereinen zwei Listen mit einzelnen Werten zu einer Liste von Tupeln oder unterteilen eine Liste von Tupeln in zwei Listen mit einzelnen Werten. Die einfachste List.zip-Funktion übernimmt zwei Listen mit einzelnen Elementen und erzeugt eine einzelne Liste von Tupelpaaren. Eine andere Version, List.zip3, übernimmt drei Listen mit einzelnen Elementen und erzeugt eine einzelne Liste von Tupeln, die jeweils drei Elemente umfassen. Das folgende Codebeispiel veranschaulicht die Verwendung von List.zip.

let list1 = [ 1; 2; 3 ]
let list2 = [ -1; -2; -3 ]
let listZip = List.zip list1 list2
printfn "%A" listZip

Die Ausgabe lautet wie folgt:

[(1, -1); (2, -2); (3; -3)]

Das folgende Codebeispiel veranschaulicht die Verwendung von List.zip3.

let list3 = [ 0; 0; 0]
let listZip3 = List.zip3 list1 list2 list3
printfn "%A" listZip3

Die Ausgabe lautet wie folgt:

[(1, -1, 0); (2, -2, 0); (3, -3, 0)]

Die entsprechenden Versionen zum Entzippen, List.unzip und List.unzip3, übernehmen Listen mit Tupeln und geben Listen in einem Tupel zurück, wobei die erste Liste alle Elemente enthält, die sich in den jeweiligen Tupeln an erster Stelle befunden haben. Die zweite Liste enthält das zweite Element der einzelnen Tupel usw.

Das folgende Codebeispiel veranschaulicht die Verwendung von LiList.unzip.

let lists = List.unzip [(1,2); (3,4)]
printfn "%A" lists
printfn "%A %A" (fst lists) (snd lists)

Die Ausgabe lautet wie folgt:

([1; 3], [2; 4])
[1; 3] [2; 4]

Das folgende Codebeispiel veranschaulicht die Verwendung von List.unzip3.

let listsUnzip3 = List.unzip3 [(1,2,3); (4,5,6)]
printfn "%A" listsUnzip3

Die Ausgabe lautet wie folgt:

([1; 4], [2; 5], [3; 6])

Arbeiten mit Listenelementen

F# unterstützt eine Vielzahl von Operationen mit Listenelementen. Die einfachste Operation ist List.iter, mit der Sie für jedes Element einer Liste eine Funktion aufrufen können. Zu den Variationen zählen List.iter2, mit der Sie eine Operation für die Elemente zweier Listen ausführen können, List.iteri, die sich mit der Ausnahme, dass der Index der einzelnen Elemente als Argument an die Funktion übergeben wird, die für die einzelnen Elemente aufgerufen wird, ähnlich wie List.iter verhält, und List.iteri2, die eine Kombination der Funktionen vonList.iter2 und List.iteri darstellt. Diese Funktionen werden im folgenden Codebeispiel veranschaulicht.

let list1 = [1; 2; 3]
let list2 = [4; 5; 6]
List.iter (fun x -> printfn "List.iter: element is %d" x) list1
List.iteri(fun i x -> printfn "List.iteri: element %d is %d" i x) list1
List.iter2 (fun x y -> printfn "List.iter2: elements are %d %d" x y) list1 list2
List.iteri2 (fun i x y ->
                printfn "List.iteri2: element %d of list1 is %d element %d of list2 is %d"
                  i x i y)
            list1 list2

Die Ausgabe lautet wie folgt:

List.iter: element is 1
List.iter: element is 2
List.iter: element is 3
List.iteri: element 0 is 1
List.iteri: element 1 is 2
List.iteri: element 2 is 3
List.iter2: elements are 1 4
List.iter2: elements are 2 5
List.iter2: elements are 3 6
List.iteri2: element 0 of list1 is 1; element 0 of list2 is 4
List.iteri2: element 1 of list1 is 2; element 1 of list2 is 5
List.iteri2: element 2 of list1 is 3; element 2 of list2 is 6

Eine weitere häufig verwendete Funktion zur Transformation von Listenelementen ist List.map, mit der Sie eine Funktion auf die einzelnen Elemente einer Liste anwenden und sämtliche Ergebnisse zu einer neuen Liste hinzufügen können. List.map2 und List.map3 sind Variationen, die mehrere Listen übernehmen. Sie können auchList.mapi und List.mapi2 verwenden, wenn der Funktion zusätzlich zum Element auch der Index der einzelnen Elemente übergeben werden muss. Der einzige Unterschied zwischen List.mapi2 und List.mapi besteht darin, dass List.mapi2 mit zwei Listen arbeitet. Das folgende Beispiel veranschaulicht List.map.

let list1 = [1; 2; 3]
let newList = List.map (fun x -> x + 1) list1
printfn "%A" newList

Die Ausgabe lautet wie folgt:

[2; 3; 4]

Im folgenden Beispiel wird die Verwendung von List.map2 veranschaulicht.

let list1 = [1; 2; 3]
let list2 = [4; 5; 6]
let sumList = List.map2 (fun x y -> x + y) list1 list2
printfn "%A" sumList

Die Ausgabe lautet wie folgt:

[5; 7; 9]

Im folgenden Beispiel wird die Verwendung von List.map3 veranschaulicht.

let newList2 = List.map3 (fun x y z -> x + y + z) list1 list2 [2; 3; 4]
printfn "%A" newList2

Die Ausgabe lautet wie folgt:

[7; 10; 13]

Im folgenden Beispiel wird die Verwendung von List.mapi veranschaulicht.

let newListAddIndex = List.mapi (fun i x -> x + i) list1
printfn "%A" newListAddIndex

Die Ausgabe lautet wie folgt:

[1; 3; 5]

Im folgenden Beispiel wird die Verwendung von List.mapi2 veranschaulicht.

let listAddTimesIndex = List.mapi2 (fun i x y -> (x + y) * i) list1 list2
printfn "%A" listAddTimesIndex

Die Ausgabe lautet wie folgt:

[0; 7; 18]

List.collect verhält sich wie List.map mit der Ausnahme, dass jedes Element eine Liste erzeugt und sämtliche Listen zu einer abschließenden Liste verkettet werden, ähnlich wie . Im folgenden Code generiert jedes Element der Liste drei Zahlen. Diese werden alle in einer Liste erfasst.

let collectList = List.collect (fun x -> [for i in 1..3 -> x * i]) list1
printfn "%A" collectList

Die Ausgabe lautet wie folgt:

[1; 2; 3; 2; 4; 6; 3; 6; 9]

Sie können auch List.filter verwenden, die eine booleschen Bedingung übernimmt und eine neue Liste erzeugt, die nur aus den Elementen besteht, die die angegebene Bedingung erfüllen.

let evenOnlyList = List.filter (fun x -> x % 2 = 0) [1; 2; 3; 4; 5; 6]

Die Ergebnisliste lautet [2; 4; 6].

List.choose ist eine Kombination aus Zuordnung und Filter, mit der Sie Elemente gleichzeitig umwandeln und auswählen können. List.choose wendet eine Funktion an, die eine Option zu den einzelnen Elementen einer Liste zurückgibt, und gibt eine neue Liste der Ergebnisse für Elemente zurück, wenn die Funktion den Optionswert Some liefert.

Der folgende Code veranschaulicht die Verwendung von List.choose, um Wörter in Großbuchstaben aus einer Wörterliste auszuwählen.

let listWords = [ "and"; "Rome"; "Bob"; "apple"; "zebra" ]
let isCapitalized (string1:string) = System.Char.IsUpper string1[0]
let results = List.choose (fun elem ->
    match elem with
    | elem when isCapitalized elem -> Some(elem + "'s")
    | _ -> None) listWords
printfn "%A" results

Die Ausgabe lautet wie folgt:

["Rome's"; "Bob's"]

Arbeiten mit mehreren Listen

Listen können verknüpft werden. Verwenden Sie List.append, um zwei Listen zu einer zu verknüpfen. Verwenden Sie List.concat, um mehr als zwei Listen zu verknüpfen.

let list1to10 = List.append [1; 2; 3] [4; 5; 6; 7; 8; 9; 10]
let listResult = List.concat [ [1; 2; 3]; [4; 5; 6]; [7; 8; 9] ]
List.iter (fun elem -> printf "%d " elem) list1to10
printfn ""
List.iter (fun elem -> printf "%d " elem) listResult

Falt- und Suchoperationen

Einige Listenoperationen umfassen gegenseitige Abhängigkeiten zwischen allen Listenelementen. Die Falt- und Suchoperationen verhalten sich wieList.iter und List.map hinsichtlich der Tatsache, dass Sie für jedes Element eine Funktion aufrufen, aber diese Operationen stellen einen zusätzlichen Parameter namens Akkumulator bereit, der Informationen über die Berechnung überträgt.

Verwenden Sie List.fold, um eine Berechnung für eine Liste durchzuführen.

Das folgende Codebeispiel veranschaulicht die Verwendung von List.fold, um verschiedene Operationen durchzuführen.

Die Liste wird durchlaufen. Der Akkumulator acc ist ein Wert, der mit voranschreitender Berechnung weitergegeben wird. Das erste Argument übernimmt den Akkumulator sowie das Listenelement und gibt das Zwischenergebnis der Berechnung für dieses Listenelement zurück. Das zweite Argument ist der Ausgangswert des Akkumulators.

let sumList list = List.fold (fun acc elem -> acc + elem) 0 list
printfn "Sum of the elements of list %A is %d." [ 1 .. 3 ] (sumList [ 1 .. 3 ])

// The following example computes the average of a list.
let averageList list = (List.fold (fun acc elem -> acc + float elem) 0.0 list / float list.Length)

// The following example computes the standard deviation of a list.
// The standard deviation is computed by taking the square root of the
// sum of the variances, which are the differences between each value
// and the average.
let stdDevList list =
    let avg = averageList list
    sqrt (List.fold (fun acc elem -> acc + (float elem - avg) ** 2.0 ) 0.0 list / float list.Length)

let testList listTest =
    printfn "List %A average: %f stddev: %f" listTest (averageList listTest) (stdDevList listTest)

testList [1; 1; 1]
testList [1; 2; 1]
testList [1; 2; 3]

// List.fold is the same as to List.iter when the accumulator is not used.
let printList list = List.fold (fun acc elem -> printfn "%A" elem) () list
printList [0.0; 1.0; 2.5; 5.1 ]

// The following example uses List.fold to reverse a list.
// The accumulator starts out as the empty list, and the function uses the cons operator
// to add each successive element to the head of the accumulator list, resulting in a
// reversed form of the list.
let reverseList list = List.fold (fun acc elem -> elem::acc) [] list
printfn "%A" (reverseList [1 .. 10])

Die Versionen dieser Funktionen, die eine Ziffer im Funktionsnamen enthalten, arbeiten mit mehreren Listen. List.fold2 führt z. B. Berechnungen mit zwei Listen durch.

Das folgende Beispiel veranschaulicht die Verwendung von List.fold2.

// Use List.fold2 to perform computations over two lists (of equal size) at the same time.
// Example: Sum the greater element at each list position.
let sumGreatest list1 list2 = List.fold2 (fun acc elem1 elem2 ->
                                              acc + max elem1 elem2) 0 list1 list2

let sum = sumGreatest [1; 2; 3] [3; 2; 1]
printfn "The sum of the greater of each pair of elements in the two lists is %d." sum

List.fold und List.scan unterscheiden sich List.fold dahingehend, dass den abschließenden Wert des zusätzlichen Parameters zurückgibt, während List.scan die Liste der Zwischenwerte (zusammen mit dem Endwert) des zusätzlichen Parameters zurückgibt.

Jede dieser Funktionen umfasst eine Umkehrvariante, z. B. List.foldBack, die sich hinsichtlich der Reihenfolge unterscheidet, in der die Liste durchlaufen wird und in der die Argumente angeordnet sind. Außerdem verfügen List.fold und List.foldBack über Variationen, List.fold2 und List.foldBack2, die zwei Listen gleicher Länge verarbeiten. Die Funktion, die für die einzelnen Elemente ausgeführt wird, kann entsprechende Elemente beider Listen verwenden, um Aktionen durchzuführen. Die Elementtypen beider Listen können wie im folgenden Beispiel unterschiedlich sein, in dem eine Liste Transaktionsbeträge für ein Bankkonto und die andere Liste den Typ der Transaktion enthält: Einzahlung oder Auszahlung.

// Discriminated union type that encodes the transaction type.
type Transaction =
    | Deposit
    | Withdrawal

let transactionTypes = [Deposit; Deposit; Withdrawal]
let transactionAmounts = [100.00; 1000.00; 95.00 ]
let initialBalance = 200.00

// Use fold2 to perform a calculation on the list to update the account balance.
let endingBalance = List.fold2 (fun acc elem1 elem2 ->
                                match elem1 with
                                | Deposit -> acc + elem2
                                | Withdrawal -> acc - elem2)
                                initialBalance
                                transactionTypes
                                transactionAmounts
printfn "%f" endingBalance

Für eine Berechnung wie bei der Addition weisen List.fold und List.foldBack denselben Effekt auf, da das Ergebnis nicht von der Reihenfolge des Durchlaufs abhängt. Im folgenden Beispiel wird List.foldBack zum Hinzufügen der Elemente zu einer Liste verwendet.

let sumListBack list = List.foldBack (fun elem acc -> acc + elem) list 0
printfn "%d" (sumListBack [1; 2; 3])

// For a calculation in which the order of traversal is important, fold and foldBack have different
// results. For example, replacing fold with foldBack in the listReverse function
// produces a function that copies the list, rather than reversing it.
let copyList list = List.foldBack (fun elem acc -> elem::acc) list []
printfn "%A" (copyList [1 .. 10])

Das folgende Beispiel kehrt zum Bankkontobeispiel zurück. Dieses Mal wird ein neuer Transaktionstyp hinzugefügt: Zinsberechnung. Der Endsaldo hängt jetzt von der Reihenfolge der Transaktionen ab.

type Transaction2 =
    | Deposit
    | Withdrawal
    | Interest

let transactionTypes2 = [Deposit; Deposit; Withdrawal; Interest]
let transactionAmounts2 = [100.00; 1000.00; 95.00; 0.05 / 12.0 ]
let initialBalance2 = 200.00

// Because fold2 processes the lists by starting at the head element,
// the interest is calculated last, on the balance of 1205.00.
let endingBalance2 = List.fold2 (fun acc elem1 elem2 ->
                                match elem1 with
                                | Deposit -> acc + elem2
                                | Withdrawal -> acc - elem2
                                | Interest -> acc * (1.0 + elem2))
                                initialBalance2
                                transactionTypes2
                                transactionAmounts2
printfn "%f" endingBalance2


// Because foldBack2 processes the lists by starting at end of the list,
// the interest is calculated first, on the balance of only 200.00.
let endingBalance3 = List.foldBack2 (fun elem1 elem2 acc ->
                                match elem1 with
                                | Deposit -> acc + elem2
                                | Withdrawal -> acc - elem2
                                | Interest -> acc * (1.0 + elem2))
                                transactionTypes2
                                transactionAmounts2
                                initialBalance2
printfn "%f" endingBalance3

Die Funktion List.reduce verhält sich ähnlich wie List.fold und List.scan, jedoch mit der Ausnahme, dass List.reduce eine Funktion übernimmt, die statt einem Argument jetzt zwei Argumente des Elementtyps verwendet (wobei eines dieser Argumente als Akkumulator dient, d. h. es speichert das Zwischenergebnis der Berechnung), anstatt einen separaten Akkumulator weiterzugeben. List.reduce beginnt mit der Bearbeitung der ersten beiden Listenelemente und verwendet dann das Ergebnis der Operation zusammen mit dem nächsten Element. Da kein separater Akkumulator mit eigenem Typ vorhanden ist, kann List.reduce nur anstelle von List.fold verwendet werden, wenn Akkumulator und Elementtyp denselben Typ aufweisen. Das folgende Codebeispiel veranschaulicht die Verwendung von List.reduce. List.reduce löst eine Ausnahme aus, wenn die bereitgestellte Liste keine Elemente enthält.

Im folgenden Code erhält der Lambdaausdruck die Argumente „2“ und „4“ und gibt „6“ zurück. Der nächste Aufruf erhält die Argumente „6“ und „10“, daher ist das Ergebnis „16“.

let sumAList list =
    try
        List.reduce (fun acc elem -> acc + elem) list
    with
       | :? System.ArgumentException as exc -> 0

let resultSum = sumAList [2; 4; 10]
printfn "%d " resultSum

Konvertieren zwischen Listen und anderen Auflistungstypen

Das List-Modul stellt Funktionen zum Konvertieren von Sequenzen in Arrays und umgekehrt bereit. Verwenden Sie List.toSeq oder List.ofSeq, um in eine Sequenz zu konvertieren bzw. eine Sequenz in einen anderen Typ zu konvertieren. Verwenden Sie List.toArray oder List.ofArray, um in ein Array zu konvertieren bzw. ein Array in einen anderen Typ zu konvertieren.

Weitere Operationen

Weitere Informationen zu weiteren Operationen mit Listen finden Sie unter dem Referenzthema Listenmodul.

Weitere Informationen