Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Eine Sequenz ist eine logische Reihe von Elementen, die alle einen Typ aufweisen. Sequenzen sind besonders nützlich, wenn Sie über eine große, sortierte Sammlung von Daten verfügen, aber nicht unbedingt erwarten, dass alle Elemente verwendet werden. Einzelne Sequenzelemente werden nur nach Bedarf berechnet, sodass eine Sequenz eine bessere Leistung bieten kann als eine Liste in Situationen, in denen nicht alle Elemente verwendet werden. Sequenzen werden durch den seq<'T> Typ dargestellt, bei dem es sich um einen Alias handelt IEnumerable<T>. Daher kann jeder .NET-Typ, der die Schnittstelle implementiert IEnumerable<T> , als Sequenz verwendet werden. Das Seq-Modul bietet Unterstützung für Manipulationen mit Sequenzen.
Sequenzausdrücke
Ein Sequenzausdruck ist ein Ausdruck, der zu einer Sequenz ausgewertet wird. Sequenzausdrücke können eine Reihe von Formen annehmen. Das einfachste Formular gibt einen Bereich an. Erstellt beispielsweise eine Sequenz mit seq { 1 .. 5 } fünf Elementen, einschließlich der Endpunkte 1 und 5. Sie können auch einen Inkrement (oder eine Dekrementierung) zwischen zwei doppelten Zeiträumen angeben. Der folgende Code erstellt z. B. die Sequenz von Vielfachen von 10.
// Sequence that has an increment.
seq { 0..10..100 }
Sequenzausdrücke bestehen aus F#-Ausdrücken, die Werte der Sequenz erzeugen. Sie können Werte auch programmgesteuert generieren:
seq { for i in 1..10 -> i * i }
Im vorherigen Beispiel wird der -> Operator verwendet, mit dem Sie einen Ausdruck angeben können, dessen Wert teil der Sequenz wird. Sie können nur verwenden -> , wenn jeder Teil des codes, der darauf folgt, einen Wert zurückgibt.
Alternativ können Sie das do Schlüsselwort mit einer optionalen yield Folgenden angeben:
seq {
for i in 1..10 do
yield i * i
}
// The 'yield' is implicit and doesn't need to be specified in most cases.
seq {
for i in 1..10 do
i * i
}
Der folgende Code generiert eine Liste von Koordinatenpaaren zusammen mit einem Index in einem Array, das das Raster darstellt. Beachten Sie, dass für den ersten for Ausdruck eine do Angabe erforderlich ist.
let (height, width) = (10, 10)
seq {
for row in 0 .. width - 1 do
for col in 0 .. height - 1 -> (row, col, row * width + col)
}
Ein if ausdruck, der in einer Sequenz verwendet wird, ist ein Filter. Um beispielsweise eine Sequenz von nur Primzahlen zu generieren, vorausgesetzt, Sie haben eine Funktion isprime vom Typ int -> bool, erstellen Sie die Sequenz wie folgt.
seq {
for n in 1..100 do
if isprime n then
n
}
Wie bereits erwähnt, ist hier erforderlich, do da es keine else Verzweigung gibt, die mit der if. Wenn Sie versuchen, sie zu verwenden ->, wird eine Fehlermeldung angezeigt, die besagt, dass nicht alle Verzweigungen einen Wert zurückgeben.
dem yield!-Schlüsselwort
Manchmal möchten Sie möglicherweise eine Abfolge von Elementen in eine andere Sequenz einschließen. Um eine Sequenz in eine andere Sequenz einzuschließen, müssen Sie das yield! Schlüsselwort verwenden:
// Repeats '1 2 3 4 5' ten times
seq {
for _ in 1..10 do
yield! seq { 1; 2; 3; 4; 5}
}
Eine andere Möglichkeit, zu denken yield! , ist, dass sie eine innere Sequenz flacht und dann in die enthaltende Sequenz einschließt.
Wenn yield! sie in einem Ausdruck verwendet wird, müssen alle anderen einzelnen Werte das yield Schlüsselwort verwenden:
// Combine repeated values with their values
seq {
for x in 1..10 do
yield x
yield! seq { for i in 1..x -> i}
}
Im vorherigen Beispiel wird der Wert x zusätzlich zu allen Werten von 1 bis zu x jedem xWert erzeugt.
Beispiele
Im ersten Beispiel wird ein Sequenzausdruck verwendet, der eine Iteration, einen Filter und einen Ertrag zum Generieren eines Arrays enthält. Dieser Code druckt eine Sequenz von Primzahlen zwischen 1 und 100 in der Konsole.
// Recursive isprime function.
let isprime n =
let rec check i =
i > n / 2 || (n % i <> 0 && check (i + 1))
check 2
let aSequence =
seq {
for n in 1..100 do
if isprime n then
n
}
for x in aSequence do
printfn "%d" x
Im folgenden Beispiel wird eine Multiplikationstabelle erstellt, die aus Tupeln aus drei Elementen besteht, die jeweils aus zwei Faktoren und dem Produkt bestehen:
let multiplicationTable =
seq {
for i in 1..9 do
for j in 1..9 -> (i, j, i * j)
}
Im folgenden Beispiel wird die Verwendung der yield! Kombination einzelner Sequenzen in eine einzelne endgültige Sequenz veranschaulicht. In diesem Fall werden die Sequenzen für jede Unterstruktur in einer binären Struktur in einer rekursiven Funktion verkettet, um die endgültige Sequenz zu erzeugen.
// Yield the values of a binary tree in a sequence.
type Tree<'a> =
| Tree of 'a * Tree<'a> * Tree<'a>
| Leaf of 'a
// inorder : Tree<'a> -> seq<'a>
let rec inorder tree =
seq {
match tree with
| Tree(x, left, right) ->
yield! inorder left
yield x
yield! inorder right
| Leaf x -> yield x
}
let mytree = Tree(6, Tree(2, Leaf(1), Leaf(3)), Leaf(9))
let seq1 = inorder mytree
printfn "%A" seq1
Verwenden von Sequenzen
Sequenzen unterstützen viele der gleichen Funktionen wie Listen. Sequenzen unterstützen auch Vorgänge wie Gruppieren und Zählen mithilfe von Funktionen zum Generieren von Schlüsseln. Sequenzen unterstützen auch vielfältigere Funktionen zum Extrahieren von Untersequencen.
Viele Datentypen, z. B. Listen, Arrays, Sätze und Zuordnungen, sind implizit Sequenzen, da sie aufzählbare Auflistungen sind. Eine Funktion, die eine Sequenz als Argument verwendet, funktioniert mit einem der allgemeinen F#-Datentypen zusätzlich zu jedem .NET-Datentyp, der implementiert wird System.Collections.Generic.IEnumerable<'T>. Vergleichen Sie dies mit einer Funktion, die eine Liste als Argument verwendet, die nur Listen verwenden kann. Der Typ seq<'T> ist eine Typ abkürzung für IEnumerable<'T>. Dies bedeutet, dass jeder Typ, der den generischen System.Collections.Generic.IEnumerable<'T>Typ implementiert, der Arrays, Listen, Sets und Zuordnungen in F# und auch die meisten .NET-Auflistungstypen implementiert, mit dem seq Typ kompatibel ist und überall verwendet werden kann, wo eine Sequenz erwartet wird.
Modulfunktionen
Das Seq-Modul im FSharp.Collections-Namespace enthält Funktionen zum Arbeiten mit Sequenzen. Diese Funktionen funktionieren auch mit Listen, Arrays, Zuordnungen und Sätzen, da alle diese Typen aufgezählt werden können und daher als Sequenzen behandelt werden können.
Erstellen von Sequenzen
Sie können Sequenzen mithilfe von Sequenzausdrücken, wie zuvor beschrieben, oder mithilfe bestimmter Funktionen erstellen.
Sie können eine leere Sequenz mithilfe von Seq.empty erstellen, oder Sie können eine Sequenz von nur einem angegebenen Element mithilfe von Seq.singleton erstellen.
let seqEmpty = Seq.empty
let seqOne = Seq.singleton 10
Mit Seq.init können Sie eine Sequenz erstellen, für die die Elemente mithilfe einer von Ihnen bereitgestellten Funktion erstellt werden. Außerdem stellen Sie eine Größe für die Sequenz bereit. Diese Funktion ist genau wie List.init, mit der Ausnahme, dass die Elemente erst erstellt werden, wenn Sie die Sequenz durchlaufen. Der folgende Code veranschaulicht die Verwendung von Seq.init.
let seqFirst5MultiplesOf10 = Seq.init 5 (fun n -> n * 10)
Seq.iter (fun elem -> printf "%d " elem) seqFirst5MultiplesOf10
Die Ausgabe lautet
0 10 20 30 40
Mithilfe von Seq.ofArray und Seq.ofList'T<> Function können Sie Sequenzen aus Arrays und Listen erstellen. Sie können jedoch auch Arrays und Listen mithilfe eines Umwandlungsoperators in Sequenzen konvertieren. Beide Techniken werden im folgenden Code gezeigt.
// Convert an array to a sequence by using a cast.
let seqFromArray1 = [| 1 .. 10 |] :> seq<int>
// Convert an array to a sequence by using Seq.ofArray.
let seqFromArray2 = [| 1 .. 10 |] |> Seq.ofArray
Mithilfe von Seq.cast können Sie eine Sequenz aus einer schwach typierten Auflistung erstellen, z. B. von denen, die in System.Collectionsdefiniert sind. Solche schwach typierten Auflistungen weisen den Elementtyp System.Object auf und werden mithilfe des nicht generischen System.Collections.Generic.IEnumerable`1 Typs aufgezählt. Der folgende Code veranschaulicht die Verwendung der Seq.cast Konvertierung einer System.Collections.ArrayList Sequenz in eine Sequenz.
open System
let arr = ResizeArray<int>(10)
for i in 1 .. 10 do
arr.Add(10)
let seqCast = Seq.cast arr
Sie können unendliche Sequenzen mithilfe der Funktion Seq.initInfinite definieren. Für eine solche Sequenz stellen Sie eine Funktion bereit, die jedes Element aus dem Index des Elements generiert. Unendliche Sequenzen sind aufgrund der faulen Auswertung möglich; Elemente werden nach Bedarf durch Aufrufen der von Ihnen angegebenen Funktion erstellt. Im folgenden Codebeispiel wird eine unendliche Abfolge von Gleitkommazahlen erzeugt, in diesem Fall die abwechselnde Reihe von Quadraten aufeinander folgender ganzzahliger Zahlen.
let seqInfinite =
Seq.initInfinite (fun index ->
let n = float (index + 1)
1.0 / (n * n * (if ((index + 1) % 2 = 0) then 1.0 else -1.0)))
printfn "%A" seqInfinite
Seq.unfold generiert eine Sequenz aus einer Berechnungsfunktion, die einen Zustand akzeptiert und transformiert, um jedes nachfolgende Element in der Sequenz zu erzeugen. Der Zustand ist nur ein Wert, der zum Berechnen der einzelnen Elemente verwendet wird, und kann sich ändern, wenn jedes Element berechnet wird. Das zweite Argument Seq.unfold ist der Anfangswert, der zum Starten der Sequenz verwendet wird.
Seq.unfold verwendet einen Optionstyp für den Zustand, mit dem Sie die Sequenz beenden können, indem Sie den None Wert zurückgeben. Der folgende Code zeigt zwei Beispiele für Sequenzen seq1 und fib, die von einem unfold Vorgang generiert werden. Die erste, seq1ist nur eine einfache Sequenz mit Zahlen bis zu 20. Die zweite, fibverwendet unfold , um die Fibonacci-Sequenz zu berechnen. Da jedes Element in der Fibonacci-Sequenz die Summe der vorherigen beiden Fibonacci-Zahlen ist, ist der Zustandswert ein Tupel, das aus den vorherigen beiden Zahlen in der Sequenz besteht. Der Anfangswert lautet (0,1), die ersten beiden Zahlen in der Sequenz.
let seq1 =
0 // Initial state
|> Seq.unfold (fun state ->
if (state > 20) then
None
else
Some(state, state + 1))
printfn "The sequence seq1 contains numbers from 0 to 20."
for x in seq1 do
printf "%d " x
let fib =
(0, 1)
|> Seq.unfold (fun state ->
let cur, next = state
if cur < 0 then // overflow
None
else
let next' = cur + next
let state' = next, next'
Some (cur, state') )
printfn "\nThe sequence fib contains Fibonacci numbers."
for x in fib do printf "%d " x
Die Ausgabe lautet wie folgt:
The sequence seq1 contains numbers from 0 to 20.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
The sequence fib contains Fibonacci numbers.
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181
Der folgende Code ist ein Beispiel, das viele der hier beschriebenen Sequenzmodulfunktionen verwendet, um die Werte unendlicher Sequenzen zu generieren und zu berechnen. Der Code kann einige Minuten dauern.
// generateInfiniteSequence generates sequences of floating point
// numbers. The sequences generated are computed from the fDenominator
// function, which has the type (int -> float) and computes the
// denominator of each term in the sequence from the index of that
// term. The isAlternating parameter is true if the sequence has
// alternating signs.
let generateInfiniteSequence fDenominator isAlternating =
if (isAlternating) then
Seq.initInfinite (fun index ->
1.0 /(fDenominator index) * (if (index % 2 = 0) then -1.0 else 1.0))
else
Seq.initInfinite (fun index -> 1.0 /(fDenominator index))
// The harmonic alternating series is like the harmonic series
// except that it has alternating signs.
let harmonicAlternatingSeries = generateInfiniteSequence (fun index -> float index) true
// This is the series of reciprocals of the odd numbers.
let oddNumberSeries = generateInfiniteSequence (fun index -> float (2 * index - 1)) true
// This is the series of recipocals of the squares.
let squaresSeries = generateInfiniteSequence (fun index -> float (index * index)) false
// This function sums a sequence, up to the specified number of terms.
let sumSeq length sequence =
sequence
|> Seq.skip 1 // skip first item (matching the original behavior)
|> Seq.truncate length // don't take more than length items
|> Seq.scan (+) 0.0 // generate running sums
|> Seq.skip 1 // skip the initial 0.0 from sequence of running sums
// This function sums an infinite sequence up to a given value
// for the difference (epsilon) between subsequent terms,
// up to a maximum number of terms, whichever is reached first.
let infiniteSum infiniteSeq epsilon maxIteration =
infiniteSeq
|> sumSeq maxIteration
|> Seq.pairwise
|> Seq.takeWhile (fun elem -> abs (snd elem - fst elem) > epsilon)
|> List.ofSeq
|> List.rev
|> List.head
|> snd
// Compute the sums for three sequences that converge, and compare
// the sums to the expected theoretical values.
let result1 = infiniteSum harmonicAlternatingSeries 0.00001 100000
printfn "Result: %f ln2: %f" result1 (log 2.0)
let pi = Math.PI
let result2 = infiniteSum oddNumberSeries 0.00001 10000
printfn "Result: %f pi/4: %f" result2 (pi/4.0)
// Because this is not an alternating series, a much smaller epsilon
// value and more terms are needed to obtain an accurate result.
let result3 = infiniteSum squaresSeries 0.0000001 1000000
printfn "Result: %f pi*pi/6: %f" result3 (pi*pi/6.0)
Suchen und Suchen von Elementen
Sequenzen unterstützen Funktionen, die mit Listen zur Verfügung stehen: Seq.exists2, Seq.find, Seq.findIndex, Seq.pick, Seq.tryFind und Seq.tryFindIndex. Die Versionen dieser Funktionen, die für Sequenzen verfügbar sind, bewerten die Sequenz nur bis zum Element, nach dem gesucht wird. Beispiele finden Sie unter "Listen".
Abrufen von Untersequences
Seq.filter und Seq.choose sind wie die entsprechenden Funktionen, die für Listen verfügbar sind, mit der Ausnahme, dass die Filterung und Auswahl erst erfolgt, wenn die Sequenzelemente ausgewertet werden.
Seq.truncate erstellt eine Sequenz aus einer anderen Sequenz, beschränkt die Sequenz jedoch auf eine bestimmte Anzahl von Elementen.
Seq.take erstellt eine neue Sequenz, die nur eine bestimmte Anzahl von Elementen vom Anfang einer Sequenz enthält. Wenn weniger Elemente in der Sequenz vorhanden sind, als Sie festlegen, Seq.take wird ein System.InvalidOperationException. Der Unterschied zwischen Seq.take und Seq.truncate ist, dass Seq.truncate kein Fehler erzeugt wird, wenn die Anzahl der Elemente kleiner als die angegebene Zahl ist.
Der folgende Code zeigt das Verhalten und die Unterschiede zwischen Seq.truncate und Seq.take.
let mySeq = seq { for i in 1 .. 10 -> i*i }
let truncatedSeq = Seq.truncate 5 mySeq
let takenSeq = Seq.take 5 mySeq
let truncatedSeq2 = Seq.truncate 20 mySeq
let takenSeq2 = Seq.take 20 mySeq
let printSeq seq1 = Seq.iter (printf "%A ") seq1; printfn ""
// Up to this point, the sequences are not evaluated.
// The following code causes the sequences to be evaluated.
truncatedSeq |> printSeq
truncatedSeq2 |> printSeq
takenSeq |> printSeq
// The following line produces a run-time error (in printSeq):
takenSeq2 |> printSeq
Die Ausgabe, bevor der Fehler auftritt, lautet wie folgt.
1 4 9 16 25
1 4 9 16 25 36 49 64 81 100
1 4 9 16 25
1 4 9 16 25 36 49 64 81 100
Mithilfe von Seq.takeWhile können Sie eine Prädikatfunktion (eine boolesche Funktion) angeben und eine Sequenz aus einer anderen Sequenz erstellen, die aus diesen Elementen der ursprünglichen Sequenz besteht, für die das Prädikat gilt true, aber vor dem ersten Element, für das das Prädikat zurückgegeben wird false, anhalten.
Seq.skip gibt eine Sequenz zurück, die eine angegebene Anzahl der ersten Elemente einer anderen Sequenz überspringt und die verbleibenden Elemente zurückgibt.
Seq.skipWhile gibt eine Sequenz zurück, die die ersten Elemente einer anderen Sequenz überspringt, solange das Prädikat zurückgibt true, und gibt dann die verbleibenden Elemente zurück, beginnend mit dem ersten Element, für das das Prädikat zurückgegeben wird false.
Das folgende Codebeispiel veranschaulicht das Verhalten und die Unterschiede zwischen Seq.takeWhile, und Seq.skipSeq.skipWhile.
// takeWhile
let mySeqLessThan10 = Seq.takeWhile (fun elem -> elem < 10) mySeq
mySeqLessThan10 |> printSeq
// skip
let mySeqSkipFirst5 = Seq.skip 5 mySeq
mySeqSkipFirst5 |> printSeq
// skipWhile
let mySeqSkipWhileLessThan10 = Seq.skipWhile (fun elem -> elem < 10) mySeq
mySeqSkipWhileLessThan10 |> printSeq
Die Ausgabe lautet wie folgt.
1 4 9
36 49 64 81 100
16 25 36 49 64 81 100
Transformieren von Sequenzen
Seq.pairwise erstellt eine neue Sequenz, in der aufeinander folgende Elemente der Eingabesequenz in Tupel gruppiert werden.
let printSeq seq1 = Seq.iter (printf "%A ") seq1; printfn ""
let seqPairwise = Seq.pairwise (seq { for i in 1 .. 10 -> i*i })
printSeq seqPairwise
printfn ""
let seqDelta = Seq.map (fun elem -> snd elem - fst elem) seqPairwise
printSeq seqDelta
Seq.windowed ist ähnlich Seq.pairwise, außer dass anstelle einer Sequenz von Tupeln eine Abfolge von Arrays erzeugt wird, die Kopien benachbarter Elemente (ein Fenster) aus der Sequenz enthalten. Sie geben die Anzahl der angrenzenden Elemente an, die in den einzelnen Arrays verwendet werden sollen.
Das folgende Codebeispiel veranschaulicht die Verwendung von Seq.windowed. In diesem Fall beträgt die Anzahl der Elemente im Fenster 3. Das Beispiel verwendet printSeq, das im vorherigen Codebeispiel definiert ist.
let seqNumbers = [ 1.0; 1.5; 2.0; 1.5; 1.0; 1.5 ] :> seq<float>
let seqWindows = Seq.windowed 3 seqNumbers
let seqMovingAverage = Seq.map Array.average seqWindows
printfn "Initial sequence: "
printSeq seqNumbers
printfn "\nWindows of length 3: "
printSeq seqWindows
printfn "\nMoving average: "
printSeq seqMovingAverage
Die Ausgabe lautet wie folgt.
Erste Sequenz:
1.0 1.5 2.0 1.5 1.0 1.5
Windows of length 3:
[|1.0; 1.5; 2.0|] [|1.5; 2.0; 1.5|] [|2.0; 1.5; 1.0|] [|1.5; 1.0; 1.5|]
Moving average:
1.5 1.666666667 1.5 1.333333333
Vorgänge mit mehreren Sequenzen
Seq.zip und Seq.zip3 nehmen zwei oder drei Sequenzen und erzeugen eine Sequenz von Tupeln. Diese Funktionen ähneln den entsprechenden Funktionen, die für Listen verfügbar sind. Es gibt keine entsprechende Funktionalität, um eine Sequenz in zwei oder mehr Sequenzen zu trennen. Wenn Sie diese Funktionalität für eine Sequenz benötigen, konvertieren Sie die Sequenz in eine Liste, und verwenden Sie List.unzip.
Sortieren, Vergleichen und Gruppieren
Die für Listen unterstützten Sortierfunktionen funktionieren auch mit Sequenzen. Dazu gehören Seq.sort und Seq.sortBy. Diese Funktionen durchlaufen die gesamte Sequenz.
Sie vergleichen zwei Sequenzen mithilfe der Funktion Seq.compareWith . Die Funktion vergleicht die aufeinander folgenden Elemente wiederum und stoppt, wenn es auf das erste ungleiche Paar trifft. Alle zusätzlichen Elemente tragen nicht zum Vergleich bei.
Der folgende Code zeigt die Verwendung von Seq.compareWith.
let sequence1 = seq { 1 .. 10 }
let sequence2 = seq { 10 .. -1 .. 1 }
// Compare two sequences element by element.
let compareSequences =
Seq.compareWith (fun elem1 elem2 ->
if elem1 > elem2 then 1
elif elem1 < elem2 then -1
else 0)
let compareResult1 = compareSequences sequence1 sequence2
match compareResult1 with
| 1 -> printfn "Sequence1 is greater than sequence2."
| -1 -> printfn "Sequence1 is less than sequence2."
| 0 -> printfn "Sequence1 is equal to sequence2."
| _ -> failwith("Invalid comparison result.")
Im vorherigen Code wird nur das erste Element berechnet und untersucht, und das Ergebnis lautet -1.
Seq.countBy verwendet eine Funktion, die einen Wert generiert, der als Schlüssel für jedes Element bezeichnet wird. Für jedes Element wird ein Schlüssel generiert, indem diese Funktion für jedes Element aufgerufen wird.
Seq.countBy gibt dann eine Sequenz zurück, die die Schlüsselwerte enthält, und eine Anzahl der Elemente, die jeden Wert des Schlüssels generiert haben.
let mySeq1 = seq { 1.. 100 }
let printSeq seq1 = Seq.iter (printf "%A ") seq1
let seqResult =
mySeq1
|> Seq.countBy (fun elem ->
if elem % 3 = 0 then 0
elif elem % 3 = 1 then 1
else 2)
printSeq seqResult
Die Ausgabe lautet wie folgt.
(1, 34) (2, 33) (0, 33)
Die vorherige Ausgabe zeigt, dass es 34 Elemente der ursprünglichen Sequenz gab, die den Schlüssel 1, 33 Werte erzeugten, die den Schlüssel 2 und 33 Werte erzeugten, die den Schlüssel 0 erzeugten.
Sie können Elemente einer Sequenz gruppieren, indem Sie Seq.groupBy aufrufen.
Seq.groupBy verwendet eine Sequenz und eine Funktion, die einen Schlüssel aus einem Element generiert. Die Funktion wird für jedes Element der Sequenz ausgeführt.
Seq.groupBy gibt eine Abfolge von Tupeln zurück, wobei das erste Element jedes Tupels der Schlüssel und die zweite eine Abfolge von Elementen ist, die diesen Schlüssel erzeugen.
Das folgende Codebeispiel zeigt die Verwendung der Seq.groupBy Partitionierung der Reihenfolge von Zahlen zwischen 1 und 100 in drei Gruppen mit den unterschiedlichen Schlüsselwerten 0, 1 und 2.
let sequence = seq { 1 .. 100 }
let printSeq seq1 = Seq.iter (printf "%A ") seq1
let sequences3 =
sequences
|> Seq.groupBy (fun index ->
if (index % 3 = 0) then 0
elif (index % 3 = 1) then 1
else 2)
sequences3 |> printSeq
Die Ausgabe lautet wie folgt.
(1, seq [1; 4; 7; 10; ...]) (2, seq [2; 5; 8; 11; ...]) (0, seq [3; 6; 9; 12; ...])
Sie können eine Sequenz erstellen, die doppelte Elemente eliminiert, indem Sie Seq.distinct aufrufen. Oder Sie können Seq.distinctBy verwenden, das eine Schlüsselgenerierungsfunktion verwendet, um für jedes Element aufgerufen zu werden. Die resultierende Sequenz enthält Elemente der ursprünglichen Sequenz mit eindeutigen Schlüsseln; spätere Elemente, die einen doppelten Schlüssel zu einem früheren Element erzeugen, werden verworfen.
Das folgende Codebeispiel veranschaulicht die Verwendung von Seq.distinct.
Seq.distinct wird veranschaulicht, indem Sequenzen generiert werden, die binäre Zahlen darstellen, und dann zeigen, dass die einzigen eindeutigen Elemente 0 und 1 sind.
let binary n =
let rec generateBinary n =
if (n / 2 = 0) then [n]
else (n % 2) :: generateBinary (n / 2)
generateBinary n
|> List.rev
|> Seq.ofList
printfn "%A" (binary 1024)
let resultSequence = Seq.distinct (binary 1024)
printfn "%A" resultSequence
Der folgende Code veranschaulicht Seq.distinctBy , indem er mit einer Sequenz beginnt, die negative und positive Zahlen enthält und die Absolute-Wert-Funktion als Schlüsselgenerierungsfunktion verwendet. Bei der resultierenden Sequenz fehlen alle positiven Zahlen, die den negativen Zahlen in der Sequenz entsprechen, da die negativen Zahlen früher in der Sequenz angezeigt werden und daher anstelle der positiven Zahlen mit demselben Absolutwert oder Schlüssel ausgewählt werden.
let inputSequence = { -5 .. 10 }
let printSeq seq1 = Seq.iter (printf "%A ") seq1
printfn "Original sequence: "
printSeq inputSequence
printfn "\nSequence with distinct absolute values: "
let seqDistinctAbsoluteValue = Seq.distinctBy (fun elem -> abs elem) inputSequence
printSeq seqDistinctAbsoluteValue
Readonly- und Cached-Sequenzen
Seq.readonly erstellt eine schreibgeschützte Kopie einer Sequenz.
Seq.readonly ist nützlich, wenn Sie über eine Lese-/Schreibzugriffsauflistung verfügen, z. B. ein Array, und Sie möchten die ursprüngliche Auflistung nicht ändern. Diese Funktion kann verwendet werden, um die Datenkapselung beizubehalten. Im folgenden Codebeispiel wird ein Typ erstellt, der ein Array enthält. Eine Eigenschaft macht das Array verfügbar, aber anstatt ein Array zurückzugeben, wird eine Sequenz zurückgegeben, die mithilfe Seq.readonlydes Arrays erstellt wird.
type ArrayContainer(start, finish) =
let internalArray = [| start .. finish |]
member this.RangeSeq = Seq.readonly internalArray
member this.RangeArray = internalArray
let newArray = new ArrayContainer(1, 10)
let rangeSeq = newArray.RangeSeq
let rangeArray = newArray.RangeArray
// These lines produce an error:
//let myArray = rangeSeq :> int array
//myArray[0] <- 0
// The following line does not produce an error.
// It does not preserve encapsulation.
rangeArray[0] <- 0
Seq.cache erstellt eine gespeicherte Version einer Sequenz. Wird Seq.cache verwendet, um eine Erneuteinwertung einer Sequenz zu vermeiden oder wenn Sie mehrere Threads verwenden, die eine Sequenz verwenden, aber Sie müssen sicherstellen, dass jedes Element nur einmal ausgeführt wird. Wenn Sie über eine Sequenz verfügen, die von mehreren Threads verwendet wird, können Sie einen Thread haben, der die Werte für die ursprüngliche Sequenz aufzählt und berechnet, und verbleibende Threads können die zwischengespeicherte Sequenz verwenden.
Ausführen von Berechnungen für Sequenzen
Einfache arithmetische Vorgänge sind wie Listen, wie "Seq.average", "Seq.sum", "Seq.averageBy", "Seq.sumBy" usw.
Seq.fold, Seq.reduce und Seq.scan sind wie die entsprechenden Funktionen, die für Listen verfügbar sind. Sequenzen unterstützen eine Teilmenge der vollständigen Variationen dieser Funktionen, die auflisten. Weitere Informationen und Beispiele finden Sie unter "Listen".