Sekwencje
Sekwencja to logiczna seria wszystkich elementów jednego typu. Sekwencje są szczególnie przydatne, gdy masz dużą, uporządkowaną kolekcję danych, ale niekoniecznie należy oczekiwać użycia wszystkich elementów. Poszczególne elementy sekwencji są obliczane tylko zgodnie z wymaganiami, więc sekwencja może zapewnić lepszą wydajność niż lista w sytuacjach, w których nie wszystkie elementy są używane. Sekwencje są reprezentowane przez seq<'T>
typ, który jest aliasem dla elementu IEnumerable<T>. W związku z tym każdy typ platformy .NET, który implementuje IEnumerable<T> interfejs, może służyć jako sekwencja. Moduł Seq zapewnia obsługę manipulacji obejmujących sekwencje.
Wyrażenia sekwencji
Wyrażenie sekwencji to wyrażenie , które daje w wyniku sekwencję. Wyrażenia sekwencji mogą przyjmować wiele formularzy. Najprostszy formularz określa zakres. Na przykład seq { 1 .. 5 }
tworzy sekwencję zawierającą pięć elementów, w tym punkty końcowe 1 i 5. Można również określić przyrost (lub dekrementację) między dwoma podwójnymi kropkami. Na przykład poniższy kod tworzy sekwencję wielokrotności 10.
// Sequence that has an increment.
seq { 0..10..100 }
Wyrażenia sekwencji składają się z wyrażeń języka F#, które generują wartości sekwencji. Można również programowo generować wartości:
seq { for i in 1..10 -> i * i }
W poprzednim przykładzie użyto ->
operatora , który umożliwia określenie wyrażenia, którego wartość stanie się częścią sekwencji. Można użyć ->
tylko wtedy, gdy każda część kodu, która następuje po nim, zwraca wartość.
Alternatywnie możesz określić do
słowo kluczowe z opcjonalnym yield
, który jest następujący:
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
}
Poniższy kod generuje listę par współrzędnych wraz z indeksem w tablicę reprezentującą siatkę. Należy pamiętać, że pierwsze for
wyrażenie wymaga określenia elementu do
.
let (height, width) = (10, 10)
seq {
for row in 0 .. width - 1 do
for col in 0 .. height - 1 -> (row, col, row * width + col)
}
Wyrażenie if
używane w sekwencji jest filtrem. Na przykład aby wygenerować sekwencję tylko liczb pierwszych, zakładając, że masz funkcję isprime
typu int -> bool
, skonstruuj sekwencję w następujący sposób.
seq {
for n in 1..100 do
if isprime n then
n
}
Jak wspomniano wcześniej, jest tu wymagany, do
ponieważ nie else
ma gałęzi, która przechodzi z gałęzią if
. Jeśli spróbujesz użyć ->
polecenia , zostanie wyświetlony błąd informujący, że nie wszystkie gałęzie zwracają wartość.
Słowo kluczowe yield!
Czasami możesz chcieć dołączyć sekwencję elementów do innej sekwencji. Aby uwzględnić sekwencję w innej sekwencji, należy użyć słowa kluczowego yield!
:
// Repeats '1 2 3 4 5' ten times
seq {
for _ in 1..10 do
yield! seq { 1; 2; 3; 4; 5}
}
Innym sposobem myślenia yield!
jest to, że spłaszcza sekwencję wewnętrzną, a następnie uwzględnia ją w sekwencji zawierającej.
Gdy yield!
jest używany w wyrażeniu, wszystkie inne pojedyncze wartości muszą używać słowa kluczowego yield
:
// Combine repeated values with their values
seq {
for x in 1..10 do
yield x
yield! seq { for i in 1..x -> i}
}
W poprzednim przykładzie zostanie wygenerowana wartość x
oprócz wszystkich wartości z 1
do x
dla każdego x
elementu .
Przykłady
W pierwszym przykładzie użyto wyrażenia sekwencji zawierającego iterację, filtr i rentowność w celu wygenerowania tablicy. Ten kod wyświetla sekwencję liczb pierwszych z zakresu od 1 do 100 do konsoli.
// 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
Poniższy przykład tworzy tabelę mnożenia składającą się z krotki trzech elementów, z których każdy składa się z dwóch czynników i produktu:
let multiplicationTable =
seq {
for i in 1..9 do
for j in 1..9 -> (i, j, i * j)
}
W poniższym przykładzie pokazano użycie funkcji yield!
do łączenia poszczególnych sekwencji w jedną ostateczną sekwencję. W takim przypadku sekwencje dla każdego poddrzewa w drzewie binarnym są łączone w funkcji cyklicznej w celu utworzenia sekwencji końcowej.
// 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
Korzystanie z sekwencji
Sekwencje obsługują wiele tych samych funkcji co listy. Sekwencje obsługują również operacje, takie jak grupowanie i liczenie przy użyciu funkcji generowania kluczy. Sekwencje obsługują również bardziej zróżnicowane funkcje do wyodrębniania podsekwencjonowania.
Wiele typów danych, takich jak listy, tablice, zestawy i mapy, są niejawnie sekwencjami, ponieważ są to kolekcje wyliczalne. Funkcja, która przyjmuje sekwencję jako argument, współpracuje z dowolnym z typowych typów danych języka F#, a także z dowolnym typem danych platformy .NET, który implementuje System.Collections.Generic.IEnumerable<'T>
element . Porównaj to z funkcją, która przyjmuje listę jako argument, który może przyjmować tylko listy. Typ seq<'T>
jest skrótem typu dla .IEnumerable<'T>
Oznacza to, że każdy typ implementujący ogólny System.Collections.Generic.IEnumerable<'T>
, który zawiera tablice, listy, zestawy i mapy w języku F#, a także większość typów kolekcji platformy .NET, jest zgodna z seq
typem i może być używana wszędzie tam, gdzie sekwencja jest oczekiwana.
Funkcje modułu
Moduł Seq w przestrzeni nazw FSharp.Collections zawiera funkcje do pracy z sekwencjami. Te funkcje działają również z listami, tablicami, mapami i zestawami, ponieważ wszystkie te typy są wyliczalne i dlatego mogą być traktowane jako sekwencje.
Tworzenie sekwencji
Sekwencje można tworzyć przy użyciu wyrażeń sekwencji, jak opisano wcześniej, lub przy użyciu niektórych funkcji.
Pustą sekwencję można utworzyć przy użyciu pliku Seq.empty lub utworzyć sekwencję tylko jednego określonego elementu przy użyciu pliku Seq.singleton.
let seqEmpty = Seq.empty
let seqOne = Seq.singleton 10
Za pomocą pliku Seq.init można utworzyć sekwencję, dla której elementy są tworzone przy użyciu podanej funkcji. Należy również podać rozmiar sekwencji. Ta funkcja jest tak samo jak List.init, z tą różnicą, że elementy nie są tworzone do momentu iteracji przez sekwencję. Poniższy kod ilustruje użycie metody Seq.init
.
let seqFirst5MultiplesOf10 = Seq.init 5 (fun n -> n * 10)
Seq.iter (fun elem -> printf "%d " elem) seqFirst5MultiplesOf10
Dane wyjściowe to
0 10 20 30 40
Korzystając z funkcji Seq.ofArray i Seq.ofList'T<>, można tworzyć sekwencje na podstawie tablic i list. Można jednak również konwertować tablice i listy na sekwencje przy użyciu operatora rzutowania. Obie techniki są wyświetlane w poniższym kodzie.
// 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
Za pomocą funkcji Seq.cast można utworzyć sekwencję na podstawie słabo typizowanej kolekcji, takiej jak te zdefiniowane w pliku System.Collections
. Takie słabo typizowane kolekcje mają typ System.Object
elementu i są wyliczane przy użyciu typu innego niż ogólny System.Collections.Generic.IEnumerable`1
. Poniższy kod ilustruje użycie metody Seq.cast
, aby przekonwertować element System.Collections.ArrayList
na sekwencję.
open System
let arr = ResizeArray<int>(10)
for i in 1 .. 10 do
arr.Add(10)
let seqCast = Seq.cast arr
Możesz zdefiniować nieskończone sekwencje przy użyciu funkcji Seq.initInfinite . W przypadku takiej sekwencji należy podać funkcję, która generuje każdy element z indeksu elementu. Nieskończone sekwencje są możliwe ze względu na leniwą ocenę; elementy są tworzone zgodnie z potrzebami przez wywołanie określonej funkcji. Poniższy przykład kodu tworzy nieskończoną sekwencję liczb zmiennoprzecinkowych, w tym przypadku przemienną serię wzajemnych kwadratów kolejnych liczb całkowitych.
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
Funkcja Seq.unfold generuje sekwencję z funkcji obliczeniowej, która przyjmuje stan i przekształca ją w celu utworzenia każdego kolejnego elementu w sekwencji. Stan jest tylko wartością używaną do obliczania każdego elementu i może ulec zmianie podczas obliczania każdego elementu. Drugi argument to Seq.unfold
początkowa wartość używana do uruchomienia sekwencji. Seq.unfold
używa typu opcji dla stanu, który umożliwia zakończenie sekwencji przez zwrócenie None
wartości. Poniższy kod przedstawia dwa przykłady sekwencji i seq1
fib
, które są generowane przez operację unfold
. Pierwsza, seq1
, jest tylko prostą sekwencją z liczbami do 20. Drugi element fib
, używa unfold
metody do obliczenia sekwencji Fibonacciego. Ponieważ każdy element w sekwencji Fibonacciego jest sumą poprzednich dwóch liczb Fibonacciego, wartość stanu jest krotką składającą się z dwóch poprzednich liczb w sekwencji. Początkowa wartość to (0,1)
, pierwsze dwie liczby w sekwencji.
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
Wynik jest następujący:
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
Poniższy kod jest przykładem, który używa wielu funkcji modułu sekwencji opisanych tutaj do generowania i obliczania wartości nieskończonych sekwencji. Uruchomienie kodu może potrwać kilka minut.
// 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 =
(0, 0.0)
|>
Seq.unfold (fun state ->
let subtotal = snd state + Seq.item (fst state + 1) sequence
if (fst state >= length) then
None
else
Some(subtotal, (fst state + 1, subtotal)))
// 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)
Wyszukiwanie i znajdowanie elementów
Sekwencje obsługują funkcje dostępne dla list: Seq.exists, Seq.exists2, Seq.find, Seq.findIndex, Seq.pick, Seq.tryFind i Seq.tryFindIndex. Wersje tych funkcji, które są dostępne dla sekwencji, oceniają sekwencję tylko do szukanego elementu. Aby zapoznać się z przykładami, zobacz Listy.
Uzyskiwanie podsekwencjonowania
Seq.filter i Seq.choose są podobne do odpowiednich funkcji, które są dostępne dla list, z tą różnicą, że filtrowanie i wybieranie nie nastąpi, dopóki elementy sekwencji nie zostaną ocenione.
Seq.truncate tworzy sekwencję z innej sekwencji, ale ogranicza sekwencję do określonej liczby elementów. Seq.take tworzy nową sekwencję zawierającą tylko określoną liczbę elementów od początku sekwencji. Jeśli sekwencja zawiera mniej elementów niż określona wartość , Seq.take
zgłasza wartość System.InvalidOperationException
. Różnica między elementami Seq.take
i Seq.truncate
polega na tym, że Seq.truncate
nie powoduje błędu, jeśli liczba elementów jest mniejsza niż określona liczba.
Poniższy kod przedstawia zachowanie i różnice między elementami Seq.truncate
i 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
Dane wyjściowe, przed wystąpieniem błędu, są następujące.
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
Używając funkcji Seq.takeWhile, można określić funkcję predykatu (funkcję logiczną) i utworzyć sekwencję z innej sekwencji składających się z tych elementów oryginalnej sekwencji, dla których predykat ma true
wartość , ale zatrzymaj przed pierwszym elementem, dla którego predykat zwraca wartość false
. Funkcja Seq.skip zwraca sekwencję, która pomija określoną liczbę pierwszych elementów innej sekwencji i zwraca pozostałe elementy. Seq.skipWhile zwraca sekwencję, która pomija pierwsze elementy innej sekwencji, o ile predykat zwraca true
wartość , a następnie zwraca pozostałe elementy, począwszy od pierwszego elementu, dla którego predykat zwraca wartość false
.
Poniższy przykład kodu ilustruje zachowanie i różnice między Seq.takeWhile
, Seq.skip
i Seq.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
Dane wyjściowe są następujące:
1 4 9
36 49 64 81 100
16 25 36 49 64 81 100
Przekształcanie sekwencji
Seq.pairwise tworzy nową sekwencję, w której kolejne elementy sekwencji wejściowej są pogrupowane w krotki.
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 jest jak Seq.pairwise
, z tą różnicą, że zamiast produkować sekwencję krotki, tworzy sekwencję tablic zawierających kopie sąsiednich elementów ( okno) z sekwencji. Należy określić liczbę sąsiednich elementów, które mają być w każdej tablicy.
W poniższym przykładzie kodu pokazano użycie metody Seq.windowed
. W tym przypadku liczba elementów w oknie wynosi 3. W przykładzie użyto metody printSeq
, która jest zdefiniowana w poprzednim przykładzie kodu.
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
Dane wyjściowe są następujące:
Sekwencja początkowa:
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
Operacje z wieloma sekwencjami
Seq.zip i Seq.zip3 zajmują dwie lub trzy sekwencje i tworzą sekwencję krotki. Te funkcje są podobne do odpowiednich funkcji dostępnych dla list. Nie ma odpowiednich funkcji, aby oddzielić jedną sekwencję na co najmniej dwie sekwencje. Jeśli potrzebujesz tej funkcji dla sekwencji, przekonwertuj sekwencję na listę i użyj polecenia List.unzip.
Sortowanie, porównywanie i grupowanie
Funkcje sortowania obsługiwane dla list działają również z sekwencjami. Obejmuje to seq.sort i Seq.sortBy. Te funkcje iterują całą sekwencję.
Porównasz dwie sekwencje przy użyciu funkcji Seq.compareWith . Funkcja porównuje kolejne elementy z kolei i zatrzymuje się, gdy napotka pierwszą nierówną parę. Żadne dodatkowe elementy nie przyczyniają się do porównania.
Poniższy kod przedstawia użycie polecenia 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.")
W poprzednim kodzie tylko pierwszy element jest obliczany i badany, a wynik wynosi -1.
Funkcja Seq.countBy generuje wartość o nazwie klucz dla każdego elementu. Klucz jest generowany dla każdego elementu przez wywołanie tej funkcji dla każdego elementu. Seq.countBy
następnie zwraca sekwencję zawierającą wartości klucza oraz liczbę elementów, które wygenerowały każdą wartość klucza.
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
Dane wyjściowe są następujące:
(1, 34) (2, 33) (0, 33)
Poprzednie dane wyjściowe pokazują, że w sekwencji oryginalnej znajdowało się 34 elementy, które wygenerowały wartości klucza 1, 33, które wygenerowały klucz 2, oraz 33 wartości, które wygenerowały klucz 0.
Elementy sekwencji można grupować, wywołując element Seq.groupBy. Seq.groupBy
pobiera sekwencję i funkcję, która generuje klucz na podstawie elementu. Funkcja jest wykonywana dla każdego elementu sekwencji. Seq.groupBy
Zwraca sekwencję krotki, gdzie pierwszy element każdej krotki jest kluczem, a drugi to sekwencja elementów tworzących ten klucz.
Poniższy przykład kodu przedstawia użycie metody Seq.groupBy
do partycjonowania sekwencji liczb z zakresu od 1 do 100 do trzech grup, które mają odrębne wartości klucza 0, 1 i 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
Dane wyjściowe są następujące:
(1, seq [1; 4; 7; 10; ...]) (2, seq [2; 5; 8; 11; ...]) (0, seq [3; 6; 9; 12; ...])
Możesz utworzyć sekwencję, która eliminuje zduplikowane elementy, wywołując funkcję Seq.distinct. Można też użyć funkcji Seq.distinctBy, która pobiera funkcję generowania kluczy do wywołania dla każdego elementu. Wynikowa sekwencja zawiera elementy oryginalnej sekwencji, które mają unikatowe klucze; późniejsze elementy, które generują zduplikowany klucz do wcześniejszego elementu, zostaną odrzucone.
Poniższy przykład kodu ilustruje użycie metody Seq.distinct
. Seq.distinct
Demonstruje się przez generowanie sekwencji reprezentujących liczby binarne, a następnie pokazano, że jedynymi odrębnymi elementami są 0 i 1.
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
Poniższy kod pokazuje Seq.distinctBy
, zaczynając od sekwencji zawierającej liczby ujemne i dodatnie oraz używając funkcji wartości bezwzględnej jako funkcji generowania kluczy. W wynikowej sekwencji brakuje wszystkich liczb dodatnich odpowiadających liczbom ujemnym w sekwencji, ponieważ liczby ujemne pojawiają się wcześniej w sekwencji i dlatego są wybierane zamiast liczb dodatnich, które mają tę samą wartość bezwzględną lub klucz.
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
Sekwencje odczytu i buforowane
Seq.readonly tworzy kopię sekwencji tylko do odczytu. Seq.readonly
jest przydatna w przypadku kolekcji odczytu i zapisu, takiej jak tablica, i nie chcesz modyfikować oryginalnej kolekcji. Ta funkcja może służyć do zachowania hermetyzacji danych. W poniższym przykładzie kodu tworzony jest typ zawierający tablicę. Właściwość uwidacznia tablicę, ale zamiast zwracać tablicę, zwraca sekwencję utworzoną na podstawie tablicy przy użyciu polecenia Seq.readonly
.
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 tworzy przechowywaną wersję sekwencji. Użyj Seq.cache
polecenia , aby uniknąć ponownej oceny sekwencji lub w przypadku wielu wątków korzystających z sekwencji, ale należy się upewnić, że każdy element działa tylko raz. Jeśli masz sekwencję używaną przez wiele wątków, możesz mieć jeden wątek, który wylicza i oblicza wartości oryginalnej sekwencji, a pozostałe wątki mogą używać sekwencji buforowanej.
Wykonywanie obliczeń w sekwencjach
Proste operacje arytmetyczne są podobne do tych z list, takich jak Seq.average, Seq.sum, Seq.averageBy, Seq.sumBy itd.
Seq.fold, Seq.reduce i Seq.scan są podobne do odpowiednich funkcji dostępnych dla list. Sekwencje obsługują podzestaw pełnych odmian tych funkcji, które obsługują listę. Aby uzyskać więcej informacji i przykładów, zobacz Listy.