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

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&#96;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 seq1fib, 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 truewartość , 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 truewartość , 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.skipi 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.

Zobacz też