Freigeben über


Listen (F#)

In F# ist eine Liste eine geordnete, unveränderliche Reihe von Elementen des gleichen Typs.

Erstellen und Initialisieren von Listen

Sie können eine Liste definieren, indem Sie die Elemente explizit durch Semikolons getrennt aufführen und in eckige Klammern einschließen, wie in der folgenden Codezeile angezeigt.

let list123 = [ 1; 2; 3 ]

Sie können auch Zeilenumbrüche zwischen Elementen einfügen. In diesem Fall sind die Semikolons optional. Die letztgenannte Syntax kann die Lesbarkeit des Codes erhöhen, wenn längere Elementinitialisierungsausdrücke vorhanden sind oder wenn Sie einen Kommentar für jedes Element einschließen möchten.

let list123 = [
    1
    2
    3 ]

Normalerweise müssen alle Listenelemente den gleichen Typ aufweisen. Eine Ausnahme ist, dass eine Liste mit als Basistyp definierten Elementen über Elemente verfügen kann, die abgeleitete Typen sind. Folgendes ist also akzeptabel, da Button und CheckBox beide von Control abgeleitet werden.

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

Sie können auch Listenelemente mit einem Bereich definieren, der durch vom Bereichsoperator getrennte ganze Zahlen angegeben ist (..), wie im folgenden Code gezeigt.

let list1 = [ 1 .. 10 ]

Sie können auch eine Liste mit einem Schleifenkonstrukt definieren, wie im folgenden Codebeispiel.

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

Eine leere Liste wird durch ein Paar von leeren eckigen Klammern angegeben.

// An empty list.
let listEmpty = []

Sie können eine Liste auch mithilfe eines Sequenzausdrucks erstellen. Weitere Informationen finden Sie unter "Sequenzausdrücke" im Abschnitt Sequenzen." Im folgenden Code wird z. B. eine Liste von Quadraten ganzer Zahlen von 1 bis 10 erstellt.

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

Operatoren zum Arbeiten mit Listen

Sie können Elemente mit dem :: (Cons)-Operator an eine Liste anfügen. Wenn list1 [2; 3; 4] ist, wird im folgenden Code list2 als [100; 2; 3; 4] erstellt.

let list2 = 100 :: list1

Sie können Listen mit kompatiblen Typen mithilfe des @-Operators verketten, wie im folgenden Code. Wenn list1 [2; 3; 4] und list2 [100; 2; 3; 4 ] ist, wird list3 vom Code als [2; 3; 4; 100; 2; 3; 4] erstellt.

let list3 = list1 @ list2

Funktionen zum Ausführen von Operationen auf Listen sind im List-Modul verfügbar.

Da Listen in F# unveränderlich sind, werden von allen ändernden Operationen neue Listen erstellt. Die vorhandenen Listen werden also nicht geändert.

Listen in F# werden als einfach verknüpfte Listen implementiert, d. h., Operationen, die nur auf den Kopf der Liste zugreifen, sind O(1) und der Elementzugriff ist O(n).

Eigenschaften

Der List-Typ 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

true, wenn die Liste über keine Elemente verfügt.

Element

'T

Das Element im angegebenen Index (nullbasiert).

Länge

int

Die Anzahl der Elemente.

Tail

'T list

Die Liste ohne das erste Element.

Im Folgenden finden Sie einige Beispiele für die Verwendung dieser Eigenschaften.

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 das Programmieren mit Listen können Sie komplexe Vorgänge mit einem geringen Umfang an Code ausführen. In diesem Abschnitt werden allgemeine Operationen für Listen beschrieben, die für funktionale Programmierung wichtig sind.

Rekursion mit Listen

Listen eignen sich in besonderem Maße für rekursive Programmiertechniken. Angenommen, eine Operation muss für jedes Element einer Liste ausgeführt werden. Sie können hierzu rekursiv vorgehen, indem Sie die Operation für den Kopf der Liste ausführen und dann das Ende der Liste, die eine kleinere Liste (die ursprüngliche Liste ohne das erste Element) ist, wieder an die nächste Ebene der Rekursion übergeben.

Zum Schreiben einer solchen rekursiven Funktion verwenden Sie im Mustervergleich den Cons-Operator (::). Dies ermöglicht Ihnen, den Kopf einer Liste von ihrem Ende zu trennen.

Im folgenden Codebeispiel wird gezeigt, wie mithilfe von Mustervergleich eine rekursive Funktion implementiert wird, die Operationen für eine Liste ausführt.

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

Der vorherige Code funktioniert gut für kleine Listen. Bei größeren Listen könnte allerdings der Stapel überlaufen. Der folgende Code erweitert den Code mit einem Akkumulatorargument, einer Standardtechnik zum Arbeiten mit rekursiven Funktionen. Die Verwendung des Akkumulatorarguments macht das Funktionsende rekursiv, wodurch Stapelspeicher eingespart 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 akzeptiert. Die erste Liste enthält die Zahlen, deren Vielfache entfernt werden. Die zweite Liste ist die Liste, von der die Zahlen entfernt werden. Im Code des folgenden Beispiels wird diese rekursive Funktion verwendet, um alle Zahlen, die keine Primzahlen sind, aus einer Liste zu entfernen, sodass das Ergebnis eine Liste von Primzahlen ist.

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 List-Modul stellt Funktionen bereit, die auf die Elemente einer Liste zugreifen. Auf das Anfangselement kann am schnellsten und einfachsten zugegriffen werden. Verwenden Sie die Head-Eigenschaft oder die Modulfunktion List.head. Sie können mit der Tail-Eigenschaft oder der List.tail-Funktion auf das Ende einer Liste zugreifen. Verwenden Sie die List.nth-Funktion, um ein Element anhand des Indexes zu suchen. List.nth durchläuft die Liste. Daher entspricht es O(n). Wenn List.nth im Code häufig verwendet wird, ist ggf. die Verwendung eines Arrays anstelle einer Liste zu empfehlen. Der Elementzugriff auf Arrays ist O(1).

Boolesche Operationen mit Listen

Die List.isEmpty-Funktion bestimmt, 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 besteht. List.exists2 ist ähnlich, aber wird auf aufeinanderfolgende Paare von Elementen in zwei Listen angewendet.

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

Auf ähnliche Weise bestimmt List.forall2, ob alle Elemente in den entsprechenden Positionen in zwei Listen einen booleschen Ausdruck zufriedenstellen, der jedes Elementepaar einschließt.

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

Die List.sort-, List.sortBy- und List.sortWith-Funktionen sortieren Listen. Die Sortierfunktion bestimmt, welche dieser drei Funktionen verwendet werden soll. List.sort verwendet den standardmäßigen generischen Vergleich. Der generische Vergleich verwendet globale Operatoren auf Grundlage der generischen Compare-Funktion zum Vergleichen von Werten. Er arbeitet effizient mit einer Vielzahl von Elementtypen zusammen, z. B. einfachen numerischen Typen, Tupeln, Datensätzen, Unterscheidungs-Unions, Listen, Arrays und allen Typen, die IComparable implementieren. Bei Typen, die IComparable implementieren, wird vom generischen Vergleich die CompareTo-Funktion verwendet. Der generische Vergleich funktioniert auch mit Zeichenfolgen. Dabei wird allerdings eine kulturunabhängige Sortierreihenfolge verwendet. Der generische Vergleich sollte nicht auf nicht unterstützte Typen angewendet werden, z. B. Funktionstypen. Die Leistung des standardmäßigen generischen Vergleichs ist für klein strukturierte Typen am besten geeignet. Erwägen Sie für größere strukturierte Typen, die verglichen und häufig sortiert werden müssen, die Implementierung von IComparable und die Bereitstellung einer effizienten Implementierung der CompareTo-Methode.

List.sortBy nimmt eine Funktion, die einen als Sortierkriterium verwendeten Wert zurückgibt, und List.sortWith nimmt eine Vergleichsfunktion als Argument. Diese letzten zwei Funktionen sind nützlich, wenn Sie mit Typen arbeiten, die keinen Vergleich unterstützen, oder wenn der Vergleich komplexere Vergleichssemantik erfordert, wie im Fall von kulturbewussten Zeichenfolgen.

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 verwendet, um zunächst ein Feld eines benutzerdefinierten Typs zu vergleichen und anschließend ein Weiteres, wenn die Werte des ersten Felds übereinstimmen.

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

Für Listen werden zahlreiche Suchoperationen unterstützt. Das einfachste Element, List.find, ermöglicht Ihnen, das erste Element aufzufinden, das mit einer angegebenen Bedingung übereinstimmt.

Im folgenden Codebeispiel wird die Verwendung von List.find veranschaulicht, um die erste Zahl in einer Liste zu suchen, die durch 5 teilbar ist.

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

Das Ergebnis ist 5.

Wenn die Elemente zunächst transformiert werden müssen, rufen Sie list.pick auf. Diese Funktion akzeptiert eine Funktion, die eine Option zurückgibt, und sucht den ersten Optionswert, der Some(x) ist. Statt das Element zurückzugeben, gibt List.pick das Ergebnis x zurück. Wenn kein entsprechendes Element gefunden wird, wird List.pickKeyNotFoundException ausgelöst. 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 verwandte Funktionen, geben einen Optionswert zurück. Die List.tryFind-Funktion gibt – sofern vorhanden – das erste Element einer Liste zurück, das eine Bedingung erfüllt. Der Optionswert None wird zurückgegeben, wenn kein Element die Bedingung erfüllt. Die Variante List.tryFindIndex gibt, wenn ein Element gefunden wird, statt des Elements selbst 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 mit Listen

Gängige arithmetische Operationen, wie "sum" und "average", sind in das List-Modul integriert. Für die Verwendung mit List.sum muss der List-Elementtyp den +-Operator unterstützen und einen Nullwert aufweisen. Alle integrierten arithmetischen Typen erfüllen diese Bedingungen. Für die Verwendung mit List.average muss der Elementtyp die Division ohne Rest unterstützen. Aus diesem Grund sind ganzzahlige Typen nicht zulässig, Gleitkommatypen jedoch sind zulässig. Die List.sumBy- und List.averageBy-Funktionen akzeptieren eine Funktion als Parameter. Die Ergebnisse dieser Funktion werden verwendet, um die Werte für die Summe oder den Durchschnitt zu berechnen.

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 mit Tupeln können mit ZIP- und UNZIP-Funktionen bearbeitet werden. Diese Funktionen kombinieren zwei Listen mit einzelnen Werte zu einer Liste von Tupeln oder trennen eine Liste von Tupeln in zwei Listen mit einzelnen Werten. Die einfachste List.zip-Funktion akzeptiert zwei Listen einzelner Elemente und erzeugt eine einzelne Liste von Tupelpaaren. Eine andere Version, List.zip3, nimmt drei Listen einzelner Elemente und erzeugt eine einzelne Liste von Tupeln mit drei Elementen. Im folgenden Codebeispiel wird die Verwendung von List.zip veranschaulicht.

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

Im folgenden Codebeispiel wird die Verwendung von List.zip3 veranschaulicht.

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 Dekomprimierungsversionen, List.unzip und List.unzip3, akzeptieren Listen von Tupeln und geben Listen in einem Tupel zurück, wobei die erste Liste alle Elemente enthält, die jeweils das erste Element in den einzelnen Tupeln waren, die zweite Liste enthält alle Elemente, die jeweils das zweite Element in den einzelnen Tupeln waren usw.

Im folgenden Codebeispiel wird die Verwendung von List.unzip veranschaulicht.

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]

Im folgenden Codebeispiel wird die Verwendung von List.unzip3 veranschaulicht.

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

Die Ausgabe lautet wie folgt:

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

Operationen mit Listenelementen

F# unterstützt vielfältige Operationen für Listenelemente. Die einfachste Operation ist List.iter, womit Sie eine Funktion für jedes Element einer Liste aufrufen können. Varianten sind List.iter2, womit Sie eine Operation für Elemente von zwei Listen ausführen können, List.iteri (dies entspricht List.iter, mit dem Unterschied, dass der Index jedes Elements als Argument an die für jedes Element aufgerufene Funktion übergeben wird) und List.iteri2. Bei dieser Funktion handelt es sich um eine Kombination der Funktionalität von List.iter2 und List.iteri. Im folgenden Codebeispiel wird die Verwendung dieser Funktionen 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

Ein andere häufig verwendete Funktion zum Transformieren von Listenelementen ist List.map, mit der eine Funktion auf jedes Element einer Liste angewendet werden kann und alle Ergebnisse in eine neue Liste gesetzt werden können. List.map2 und List.map3 sind Variationen, die mehrere Listen akzeptieren. Sie können auch List.mapi und List.mapi2 verwenden, wenn der Funktion zusätzlich zum Element der Index jedes Elements übergeben werden muss. Der einzige Unterschied zwischen List.mapi2 und List.mapi ist, dass List.mapi2 mit zwei Listen funktioniert. List.map wird anhand des folgenden Beispiels veranschaulicht.

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 entspricht List.map, außer, dass jedes Element eine Liste erzeugt und alle diese Listen zu einer endgültigen Liste verkettet werden. Im folgenden Code werden von jedem Element der Liste drei Zahlen generiert. Diese werden alle in einer Liste gesammelt.

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. Diese Funktion nimmt eine boolesche Bedingung und erzeugt eine neue Liste, die nur aus Elementen besteht, die die angegebene Bedingung erfüllen.

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

Die resultierende Liste ist [2; 4; 6].

Eine Kombination aus Zuordnung und Filter (List.choose) ermöglicht Ihnen, Elemente zu transformieren und zur gleichen Zeit auszuwählen. List.choose wendet eine Funktion an, die an jedes Element einer Liste eine Option zurückgibt, und gibt eine neue Liste der Ergebnisse für Elemente zurück, wenn die Funktion den Some-Optionswert zurückgibt.

Im folgenden Code wird die Verwendung von List.choose veranschaulicht, um in einer Liste von Wörtern großgeschriebene Wörter 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"]

Operationen mit mehreren Listen

Listen können zusammengefügt werden. Verwenden Sie List.append, um zwei Listen zu einer einzigen Liste zusammenzufügen. Verwenden Sie List.concat, um mehr als zwei Listen zusammenzufügen.

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

Aufteilungs- und Überprüfungsoperationen

Bei einigen Listenoperationen bestehen Abhängigkeiten zwischen allen Listenelementen. Die Aufteilungs- und Überprüfungsoperationen entsprechen insofern List.iter und List.map, als Sie für jedes Element eine Funktion aufrufen. Diese Operationen stellen jedoch einen zusätzlichen Parameter, den Akkumulator, bereit, in dem während der gesamten Berechnung Informationen gespeichert werden.

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

Im folgenden Codebeispiel wird die Verwendung von List.fold zum Ausführen verschiedener Operationen veranschaulicht.

Die Liste wird durchlaufen. Der Akkumulator acc ist ein Wert, der bei fortschreitender Berechnung weiter übergeben wird. Das erste Argument akzeptiert den Akkumulator und das Listenelement und gibt das Zwischenergebnis der Berechnung für das Listenelement zurück. Das zweite Argument ist der Anfangswert 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 mit einer Ziffer im Funktionsnamen behandeln mehrere Listen. Beispielsweise werden mit List.fold2 Berechnungen für zwei Listen ausgeführt.

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 darin, dass List.fold den endgültigen Wert des zusätzlichen Parameters zurückgibt, aber List.scan die Liste der Zwischenwerte (zusammen mit dem endgültigen Wert) des zusätzlichen Parameters zurückgibt.

Jede dieser Funktionen verfügt über eine umgekehrte Variante, z. B. List.foldBack, die sich in der Reihenfolge der Argumente und in der Reihenfolge, in der die Liste durchlaufen wird, unterscheidet. Auch verfügen List.fold und List.foldBack über Variationen, List.fold2 und List.foldBack2, die zwei Listen gleicher Länge akzeptieren. Die Funktion, die für jedes Element ausgeführt wird, kann entsprechende Elemente beider Listen aufführen, um eine Aktion auszuführen. Die Elementtypen der zwei Listen können sich unterscheiden, wie das folgende Beispiel zeigt: Eine Liste enthält Transaktionsmengen für ein Bankkonto. Die andere Liste enthält den Typ der Transaktion: Einzahlung oder Abhebung.

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

Bei einer Berechnung, z. B. einer Addition, haben List.fold und List.foldBack die gleichen Auswirkungen, da das Ergebnis nicht von der Reihenfolge des Durchlaufs abhängt. Im folgenden Beispiel wird List.foldBack verwendet, um einer Liste Elemente hinzuzufügen.

let sumListBack list = List.foldBack (fun acc elem -> 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 wird beim Bankkontobeispiel zurückgegeben. Dieses Mal wird ein neuer Transaktionstyp hinzugefügt: eine 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 List.reduce-Funktion entspricht List.fold und List.scan, außer dass, statt dem Herumreichen eines separaten Akkumulators, von List.reduce eine Funktion genommen wird, die zwei Argumente des Elementtyps statt nur einem akzeptiert, und eines dieser Argumente als Akkumulator funktioniert. Das bedeutet, dass das Zwischenergebnis der Berechnung gespeichert wird. List.reduce beginnt mit der Verarbeitung der ersten zwei Listenelemente und verwendet das Ergebnis der Operation beim nächsten Element. Da kein separater Akkumulator vorhanden ist, der einen eigenen Typ aufweist, kann List.reduce nur anstelle von List.fold verwendet werden, wenn der Akkumulator und der Elementtyp den gleichen Typ aufweisen. Das folgende Codebeispiel veranschaulicht die Verwendung von List.reduce. List.reduce löst eine Ausnahme aus, wenn die bereitgestellte Liste über keine Elemente verfügt.

Im folgenden Code werden dem ersten Aufruf des Lambda-Ausdrucks die Argumente 2 und 4 gegeben und 6 zurückgegeben. Dem nächsten Aufruf werden die Argumente 6 und 10 gegeben, deshalb 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 zu und von Sequenzen und Arrays bereit. Zum Konvertieren zu oder von einer Sequenz verwenden Sie List.toSeq oder List.ofSeq. Zu Konvertieren zu oder von einem Array, verwenden sie List.toArray oder List.ofArray.

Zusätzliche Operationen

Informationen zu zusätzlichen Operationen mit Listen finden Sie im Bibliotheksreferenzthema Collections.List-Modul (F#).

Siehe auch

Referenz

Sequenzen (F#)

Optionen (F#)

Weitere Ressourcen

F#-Sprachreferenz

F#-Typen

Arrays (F#)

Änderungsprotokoll

Datum

Versionsgeschichte

Grund

Oktober 2010

Ausgabe eines der Codebeispiele korrigiert.

Kundenfeedback.

April 2011

Informationen zur Empty-Eigenschaft im Abschnitt Eigenschaften korrigiert.

Kundenfeedback.