Sequenzen (F#)
Eine Sequenz ist eine logische Reihe von Elementen eines einzigen Typs. Sequenzen sind besonders nützlich, wenn eine große geordnete Datenauflistung vorliegt, aber nicht alle Elemente verwendet werden sollen. Einzelne Sequenzelemente werden nur nach Bedarf berechnet, deshalb kann in Situationen, in denen nicht alle Elemente verwendet werden, mit einer Sequenz eine bessere Leistung erzielt werden als mit einer Liste. Sequenzen werden durch den seq<'T>-Typ dargestellt, der ein Alias für IEnumerable<T> ist. Daher kann jeder .NET Framework-Typ, der System.IEnumerable implementiert, als Sequenz verwendet werden. Das Seq-Modul unterstützt Manipulationen mit Sequenzen.
Sequenzausdrücke
Ein Sequenzausdruck ist ein Ausdruck, der eine Sequenz ergibt. Sequenzausdrücke können verschiedene Formate aufweisen. In der einfachsten Form wird ein Bereich angegeben. seq { 1 .. 5 } erstellt z. B. eine Sequenz, die fünf Elemente enthält, einschließlich den Endpunkten 1 und 5. Sie können auch ein Inkrement (oder Dekrement) zwischen zwei doppelten Zeiträumen angeben. Vom folgenden Code wird z. B. die Sequenz der Vielfache von 10 erstellt.
// Sequence that has an increment.
seq { 0 .. 10 .. 100 }
Sequenzausdrücke bestehen aus F#-Ausdrücken, die Werte der Sequenz erzeugen. Mithilfe des yield-Schlüsselworts können Sie Werte erzeugen, die Teil dieser Sequenz werden.
Beachten Sie folgendes Beispiel.
seq { for i in 1 .. 10 do yield i * i }
Sie können den ->-Operator statt des yield-Elements verwenden. In diesem Fall kann, wie im folgenden Beispiel gezeigt, das do-Schlüsselwort weggelassen werden.
seq { for i in 1 .. 10 -> i * i }
Im folgenden Code wird eine Liste von Koordinatenpaaren mit einem Index in einem Array generiert, das das Raster darstellt.
let (height, width) = (10, 10)
seq { for row in 0 .. width - 1 do
for col in 0 .. height - 1 do
yield (row, col, row*width + col)
}
Ein in einer Sequenz verwendeter if-Ausdruck ist ein Filter. Angenommen, es liegt eine isprime-Funktion des int -> bool-Typs vor. In diesem Fall konstruieren Sie zum Erstellen einer Primzahlsequenz die Sequenz wie folgt.
seq { for n in 1 .. 100 do if isprime n then yield n }
Wenn Sie in einer Iteration yield oder -> verwenden, sollte jede Iteration ein einzelnes Element der Sequenz generieren. Wenn jede Iteration eine Sequenz von Elementen erzeugt, verwenden Sie yield!. In diesem Fall sind die in jeder Iteration generierten Elemente verkettet, um die endgültige Sequenz zu bilden.
Sie können mehrere Ausdrücke in einem Sequenzausdruck kombinieren. Die von jedem Ausdruck generierten Elemente werden verkettet. Ein Beispiel finden Sie im Beispielabschnitt dieses Themas.
Beispiele
Im ersten Beispiel wird ein Sequenzausdruck mit einer Iteration, einem Filter und einem yield-Schlüsselwort verwendet, um ein Array zu erstellen. Dieser Code gibt eine Sequenz der Primzahlen von 1 bis 100 an die Konsole aus.
// 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 yield n }
for x in aSequence do
printfn "%d" x
Im folgenden Code wird mit yield eine Multiplikationstabelle erstellt, die aus Tupeln von drei Elementen besteht, bei denen es sich um jeweils zwei Faktoren und das Produkt handelt.
let multiplicationTable =
seq { for i in 1..9 do
for j in 1..9 do
yield (i, j, i*j) }
Im folgenden Beispiel wird veranschaulicht, wie mit yield! einzelne Sequenzen zu einer einzigen endgültigen Sequenz zusammengefasst werden. In diesem Fall werden die Sequenzen für jede Teilstruktur 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 derselben Funktionen wie Listen. Sequenzen unterstützen auch Operationen, wie Gruppieren und Zählen mithilfe von Schlüssel generierenden Funktionen. Sequenzen unterstützen auch unterschiedlichere Funktionen zum Extrahieren von Untersequenzen.
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 akzeptiert, arbeitet zusätzlich zu allen .NET Framework-Datentypen, die IEnumerable<T> implementieren, mit allen allgemeinen F#-Datentypen. Vergleichen Sie diese Funktion mit einer Funktion, die eine Liste als Argument akzeptiert, die nur Listen akzeptieren kann. Der seq<'a>-Typ ist eine Typabkürzung für IEnumerable<'a>. Dies bedeutet, dass jeder Typ, der das generische IEnumerable<T>-Element implementiert, das Arrays, Listen, Sätze und Zuordnungen in F# sowie auch die meisten .NET Framework-Auflistung enthält, mit dem seq-Typ kompatibel ist und überall dort verwendet werden kann, wo eine Sequenz erwartet wird.
Modulfunktionen
Das Seq-Modul im Microsoft.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 aufzählbar sind und daher als Sequenzen behandelt werden können.
Erstellen von Sequenzen
Wie bereits beschrieben, können Sie Sequenzen mit Sequenzausdrücken oder mit bestimmten Funktionen erstellen.
Sie können mit Seq.empty eine leere Sequenz erstellen, oder Sie können mit Seq.singleton eine Sequenz mit nur einem angegebenen Element erstellen.
let seqEmpty = Seq.empty
let seqOne = Seq.singleton 10
Sie können mithilfe von Seq.init eine Sequenz erstellen, für die die Elemente mit einer vom Benutzer bereitgestellten Funktion erstellt werden. Sie stellen auch eine Größe für die Sequenz bereit. Diese Funktion entspricht List.init, außer, dass die Elemente erst erstellt werden, wenn eine Sequenz durchlaufen wird. Das folgende Codebeispiel veranschaulicht die Verwendung von Seq.init.
let seqFirst5MultiplesOf10 = Seq.init 5 (fun n -> n * 10)
Seq.iter (fun elem -> printf "%d " elem) seqFirst5MultiplesOf10
Ausgabe:
0 10 20 30 40
Mit Seq.ofArray und Seq.ofList<'T>-Funktion (F#) können Sie Sequenzen aus Arrays und Listen erstellen. Sie können Arrays und Listen jedoch auch mit einem Umwandlungsoperator in Sequenzen konvertieren. Beide Techniken werden im folgenden Code veranschaulicht.
// 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
Mit Seq.cast können Sie eine Sequenz aus einer schwach typisierten Auflistung erstellen, wie z. B. den in System.Collections definierten Auflistungen. Solche schwach typisierten Auflistungen weisen den Object-Elementtyp auf und werden mithilfe des nicht generischen IEnumerable<T>-Typs aufgelistet. Im folgenden Code wird die Verwendung von Seq.cast zum Konvertieren des ArrayList-Elements in eine Sequenz dargestellt.
open System
let mutable arrayList1 = new System.Collections.ArrayList(10)
for i in 1 .. 10 do arrayList1.Add(10) |> ignore
let seqCast : seq<int> = Seq.cast arrayList1
Sie können unendliche Sequenzen mit der Seq.initInfinite-Funktion definieren. Für solch eine Sequenz stellen Sie eine Funktion bereit, die jedes Element aus dem Index des Elements generiert. Unendliche Sequenzen sind aufgrund der verzögerten Auswertung möglich. Elemente werden durch Aufrufen der angegebenen Funktion nach Bedarf erstellt. Im folgenden Codebeispiel wird eine unendliche Sequenz aus Gleitkommazahlen erzeugt. In diesem Fall handelt es sich um die abwechselnde Reihe der Kehrwerte der Quadrate aufeinanderfolgender ganzer 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 transformiert, um die nachfolgenden Elemente der Sequenz zu erzeugen. Der Zustand ist lediglich ein Wert, der verwendet wird, um die jeweiligen Elemente zu berechnen, und kann sich bei der Berechnung der einzelnen Elemente ändern. Das zweite Argument für Seq.unfold ist der Anfangswert, der für den Anfang der Sequenz verwendet wird. Seq.unfold verwendet einen Optionstyp für den Zustand, mit dem durch Zurückgeben des None-Werts die Sequenz beendet wird. Der folgende Code enthält zwei Sequenzbeispiele, die von einer unfold-Operation generiert werden: seq1 und fib. Das erste Element, seq1, ist nur eine einfache Sequenz mit Zahlen bis 100. Das zweite fib-Element berechnet die Fibonacci-Sequenz mithilfe von unfold. Da jedes Element in der Fibonacci-Sequenz die Summe der vorherigen zwei Fibonacci-Zahlen ist, ist der Zustandswert ein Tupel, das aus den vorherigen zwei Zahlen in der Sequenz besteht. Der Anfangswert ist (1,1), die ersten zwei Zahlen in der Sequenz.
let seq1 = Seq.unfold (fun state -> if (state > 20) then None else Some(state, state + 1)) 0
printfn "The sequence seq1 contains numbers from 0 to 20."
for x in seq1 do printf "%d " x
let fib = Seq.unfold (fun state ->
if (snd state > 1000) then None
else Some(fst state + snd state, (snd state, fst state + snd state))) (1,1)
printfn "\nThe sequence fib contains Fibonacci numbers."
for x in fib do printf "%d " x
Die Ausgabe lautet wie folgt:
Die Sequenz "seq1" enthält Zahlen von 0 bis 20.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Die Sequenz enthält Fibonacci-Zahlen.
2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
Der folgende Code ist ein Beispiel, in dem viele der hier beschriebenen Sequenzmodulfunktionen verwendet werden, um Werte unendlicher Sequenzen zu generieren und zu berechnen. Das Ausführen des Codes kann einige Minuten in Anspruch nehmen.
// infiniteSequences.fs
// 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 series is the series of reciprocals of whole numbers.
let harmonicSeries = generateInfiniteSequence (fun index -> float index) false
// 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 =
Seq.unfold (fun state ->
let subtotal = snd state + Seq.nth (fst state + 1) sequence
if (fst state >= length) then None
else Some(subtotal,(fst state + 1, subtotal))) (0, 0.0)
// 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 Finden von Elementen
Sequenzen unterstützen in Listen verfügbare Funktionen: Seq.exists, Seq.exists2, Seq.find, Seq.findIndex, Seq.pick, Seq.tryFind und Seq.tryFindIndex. Die Versionen dieser Funktionen, die für Sequenzen verfügbar sind, werten die Sequenz nur bis zu dem Element aus, nach dem gesucht wird. Beispiele finden Sie unter Listen.
Abrufen von Untersequenzen
Seq.filter und Seq.choose entsprechen den entsprechenden für Listen verfügbaren Funktionen, außer, dass Filterung und Auswahl erst erfolgen, wenn die Sequenzelemente ausgewertet wurden.
Seq.truncate erstellt eine Sequenz aus einer anderen Sequenz, aber schränkt die Sequenz auf eine angegebene Anzahl von Elementen ein. Seq.take erstellt eine neue Sequenz, die nur eine angegebene Anzahl von Elementen vom Beginn einer Sequenz enthält. Wenn es weniger Elemente in der Sequenz gibt, als zur Entnahme angeben, löst Seq.take eine InvalidOperationException aus. Der Unterschied zwischen Seq.take und Seq.truncate liegt darin, dass Seq.truncate keinen Fehler erzeugt, wenn die Anzahl der Elemente geringer als die angegebene Zahl ist.
Im folgenden Code werden das Verhalten von und die Unterschiede zwischen Seq.truncate und Seq.take veranschaulicht.
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
Bevor der Fehler auftritt, erfolgt die Ausgabe folgendermaßen.
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
Mit dem Seq.takeWhile-Element können Sie eine Prädikatfunktion (eine boolesche Funktion) angeben und eine Sequenz aus einer anderen Sequenz erstellen lassen, die sich aus den Elementen der ursprünglichen Sequenz zusammensetzt, deren Prädikat true ist, aber vor dem ersten Element anhalten, für das das Prädikat false zurückgibt. 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 true zurückgibt, und anschließend die verbleibenden Elemente beginnend mit dem ersten Element zurückgibt, für das das Prädikat false zurückgibt.
Im folgenden Codebeispiel werden das Verhalten von und die Unterschiede zwischen Seq.takeWhile, Seq.skip und Seq.skipWhile veranschaulicht.
// 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 aufeinanderfolgende 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
Seq.windowed entspricht Seq.pairwise, außer dass statt einer Sequenz von Tupeln eine Sequenz von Arrays erzeugt wird, die Kopien von benachbarten Elementen (ein Fenster) der Sequenz enthalten. Sie geben die Anzahl der in jedem Array gewünschten benachbarten Elemente an.
Im folgenden Codebeispiel wird die Verwendung von Seq.windowed veranschaulicht. In diesem Fall befinden sich drei Elemente im Fenster. Im Beispiel wird printSeq verwendet, was im vorherigen Codebeispiel definiert wurde.
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.
Anfangssequenz:
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
Operationen mit mehreren Sequenzen
Seq.zip und Seq.zip3 entnehmen zwei oder drei Sequenzen und erzeugen eine Sequenz von Tupeln. Diese Funktionen sind wie die entsprechenden Funktionen für Listen verfügbar. Es gibt keine entsprechende Funktionalität für das Trennen einer Sequenz in zwei oder mehr Sequenzen. 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. Dies beinhaltet auch Seq.sort und Seq.sortBy. Diese Funktionen durchlaufen die ganze Sequenz.
Mit der Seq.compareWith-Funktion werden zwei Funktionen verglichen. Die Funktion vergleicht aufeinanderfolgende Elemente und hält beim Treffen auf das erste ungleiche Paar an. Alle zusätzlichen Elemente tragen nicht zum Vergleich bei.
Im folgenden Code wird die Verwendung von Seq.compareWith veranschaulicht:
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 wurde nur das erste Element berechnet und untersucht. Das Ergebnis ist -1.
Seq.countBy entnimmt eine Funktion, die für jedes Element einen Schlüssel-Wert erstellt. Ein Schlüssel wird für jedes Element generiert, indem diese Funktion für jedes Element aufgerufen wird. Seq.countBy gibt dann eine Sequenz zurück, die die Schlüsselwerte enthält, sowie die Anzahl der Elemente, aus denen die jeweiligen Werte des Schlüssels generiert wurden.
let mySeq1 = seq { 1.. 100 }
let printSeq seq1 = Seq.iter (printf "%A ") seq1; printfn ""
let seqResult = Seq.countBy (fun elem -> if elem % 3 = 0 then 0
elif elem % 3 = 1 then 1
else 2) mySeq1
printSeq seqResult
Die Ausgabe lautet wie folgt.
(1, 34) (2, 33) (0, 33)
Die vorherige Ausgabe zeigt an, dass 34 Elemente der ursprünglichen Sequenz den Schlüssel "1", 33 Werte den Schlüssel "2" und 33 Werte den Schlüssel "0" erzeugt haben.
Sie können Elemente einer Sequenz gruppieren, indem Sie Seq.groupBy aufrufen. Seq.groupBy entnimmt eine Sequenz und eine Funktion, die einen Schlüssel aus einem Element erstellt. Die Funktion wird für jedes Element der Sequenz ausgeführt. Seq.groupBy gibt eine Sequenz von Tupeln zurück, wobei das erste Element jedes Tupels der Schlüssel ist, und das Zweite eine Sequenz von Elementen ist, die diesen Schlüssel erzeugen.
Im folgenden Codebeispiel wird die Verwendung von Seq.groupBy zur Partitionierung der Sequenz der Zahlen von 1 bis 100 in drei Gruppen mit den unterschiedlichen Schlüsselwerten "0", "1" und "2" dargestellt.
let sequence = seq { 1 .. 100 }
let printSeq seq1 = Seq.iter (printf "%A ") seq1; printfn ""
let sequences3 = Seq.groupBy (fun index ->
if (index % 3 = 0) then 0
elif (index % 3 = 1) then 1
else 2) sequence
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 durch Aufrufen von Seq.distinct ausschließt. Oder Sie können das Seq.distinctBy-Element verwenden, das eine Schlüssel generierende Funktion für den Aufruf für jedes Element entnimmt. Die resultierende Sequenz enthält Elemente der ursprünglichen Sequenz, die über eindeutige Schlüssel verfügen. Nachfolgende Elemente, die einen doppelten Schlüssel für ein vorheriges Element erzeugen, werden verworfen.
Das folgende Codebeispiel veranschaulicht die Verwendung von Seq.distinct. Seq.distinct wird durch das Generieren von Sequenzen, die binäre Zahlen darstellen, und den Beweis dafür veranschaulicht, dass die einzigen unterschiedlichen 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
Im folgenden Code wird Seq.distinctBy veranschaulicht, indem mit einer Sequenz begonnen wird, die negative und positive Zahlen enthält, und dabei die Funktion für den absoluten Wert als Schlüssel generierende Funktion verwendet wird. Der resultierenden Sequenz fehlen alle positiven Zahlen, die den negativen Zahlen in der Sequenz entsprechen, da die negativen Zahlen vorher in der Sequenz erscheinen und daher statt der positiven Zahlen ausgewählt werden, die den gleichen absoluten Wert oder Schlüssel aufweisen.
let inputSequence = { -5 .. 10 }
let printSeq seq1 = Seq.iter (printf "%A ") seq1; printfn ""
printfn "Original sequence: "
printSeq inputSequence
printfn "\nSequence with distinct absolute values: "
let seqDistinctAbsoluteValue = Seq.distinctBy (fun elem -> abs elem) inputSequence
seqDistinctAbsoluteValue |> printSeq
Schreibgeschützte und zwischengespeicherte Sequenzen
Seq.readonly erstellt eine schreibgeschützte Kopie einer Sequenz. Seq.readonly ist nützlich, wenn eine Lese-/Schreibauflistung vorliegt, z. B. ein Array, und die ursprüngliche Auflistung nicht geändert werden soll. 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 statt einem Array wird eine Sequenz zurückgegeben, die mit Seq.readonly aus dem Array 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. Verwenden Sie Seq.cache, um die Neuauswertung einer Sequenz zu vermeiden, oder wenn Sie mehrere Threads besitzen, die eine Sequenz verwenden. Sie müssen jedoch sicherstellen, dass jedes Element nur einmal verarbeitet wird. Wenn eine von mehreren Threads verwendete Sequenz vorliegt, kann ein Thread verwendet werden, der die Werte für die ursprüngliche Sequenz auflistet und berechnet, und die übrigen Threads können die zwischengespeicherte Sequenz verwenden.
Ausführen von Berechnungen für Sequenzen
Einfache arithmetische Operationen entsprechen denen von Listen, z. B. Seq.average, Seq.sum, Seq.averageBy, Seq.sumBy usw.
Seq.fold, Seq.reduce und Seq.scan entsprechen den entsprechenden für Listen verfügbaren Funktionen. Sequenzen unterstützen eine Teilmenge der vollständigen Variationen dieser Funktionen, die Listen unterstützen. Weitere Informationen und Beispiele finden Sie unter Listen (F#).
Siehe auch
Referenz
Weitere Ressourcen
Änderungsprotokoll
Datum |
Versionsgeschichte |
Grund |
---|---|---|
Mai 2010 |
Einige Codebeispiele wurden verbessert. |
Informationsergänzung. |