Dela via


Sekvenser

En sekvens är en logisk serie med element av en enda typ. Sekvenser är särskilt användbara när du har en stor, ordnad datainsamling men inte nödvändigtvis förväntar dig att använda alla element. Enskilda sekvenselement beräknas endast efter behov, så en sekvens kan ge bättre prestanda än en lista i situationer där inte alla element används. Sekvenser representeras av seq<'T> typen, som är ett alias för IEnumerable<T>. Därför kan alla .NET-typer som implementerar IEnumerable<T> gränssnitt användas som en sekvens. Seq-modulen ger stöd för manipuleringar som involverar sekvenser.

Sekvensuttryck

Ett sekvensuttryck är ett uttryck som utvärderas till en sekvens. Sekvensuttryck kan ta ett antal former. Det enklaste formuläret anger ett intervall. Skapar till exempel seq { 1 .. 5 } en sekvens som innehåller fem element, inklusive slutpunkterna 1 och 5. Du kan också ange en ökning (eller minskning) mellan två dubbla perioder. Följande kod skapar till exempel sekvensen med multiplar av 10.

// Sequence that has an increment.
seq { 0..10..100 }

Sekvensuttryck består av F#-uttryck som genererar värden för sekvensen. Du kan också generera värden programmatiskt:

seq { for i in 1..10 -> i * i }

I föregående exempel används operatorn -> , som gör att du kan ange ett uttryck vars värde ska bli en del av sekvensen. Du kan bara använda -> om varje del av koden som följer returnerar ett värde.

Du kan också ange nyckelordet do med ett valfritt yield som följer:

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
}

Följande kod genererar en lista över koordinatpar tillsammans med ett index till en matris som representerar rutnätet. Observera att det första for uttrycket kräver att ett do anges.

let (height, width) = (10, 10)

seq {
    for row in 0 .. width - 1 do
        for col in 0 .. height - 1 -> (row, col, row * width + col)
}

Ett if uttryck som används i en sekvens är ett filter. Om du till exempel bara vill generera en sekvens med primära tal, förutsatt att du har en funktion isprime av typen int -> bool, skapar du sekvensen enligt följande.

seq {
    for n in 1..100 do
        if isprime n then
            n
}

Som tidigare do nämnts krävs här eftersom det inte finns någon else gren som följer med if. Om du försöker använda ->får du ett felmeddelande om att inte alla grenar returnerar ett värde.

Nyckelordet yield!

Ibland kanske du vill inkludera en sekvens med element i en annan sekvens. Om du vill inkludera en sekvens i en annan sekvens måste du använda nyckelordet yield! :

// Repeats '1 2 3 4 5' ten times
seq {
    for _ in 1..10 do
        yield! seq { 1; 2; 3; 4; 5}
}

Ett annat sätt att tänka på yield! är att den plattar ut en inre sekvens och sedan inkluderar den i den innehållande sekvensen.

När yield! används i ett uttryck måste alla andra enkla värden använda nyckelordet yield :

// Combine repeated values with their values
seq {
    for x in 1..10 do
        yield x
        yield! seq { for i in 1..x -> i}
}

I föregående exempel skapas värdet x för utöver alla värden från 1 till x för varje x.

Exempel

I det första exemplet används ett sekvensuttryck som innehåller en iteration, ett filter och en avkastning för att generera en matris. Den här koden skriver ut en sekvens med primtal mellan 1 och 100 till konsolen.

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

I följande exempel skapas en multiplikationstabell som består av tupplar med tre element, som var och en består av två faktorer och produkten:

let multiplicationTable =
    seq {
        for i in 1..9 do
            for j in 1..9 -> (i, j, i * j)
    }

I följande exempel visas användningen av yield! för att kombinera enskilda sekvenser i en enda slutsekvens. I det här fallet sammanfogas sekvenserna för varje underträd i ett binärt träd i en rekursiv funktion för att skapa den sista sekvensen.

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

Använda sekvenser

Sekvenser stöder många av samma funktioner som listor. Sekvenser stöder även åtgärder som gruppering och räkning med hjälp av nyckelgenererande funktioner. Sekvenser har också stöd för mer olika funktioner för att extrahera underfrågor.

Många datatyper, till exempel listor, matriser, uppsättningar och kartor, är implicit sekvenser eftersom de är uppräkningsbara samlingar. En funktion som tar en sekvens som ett argument fungerar med någon av de vanliga F#-datatyperna, förutom alla .NET-datatyper som implementerar System.Collections.Generic.IEnumerable<'T>. Jämför detta med en funktion som tar en lista som ett argument, som bara kan ta listor. Typen seq<'T> är en typförkortning för IEnumerable<'T>. Det innebär att alla typer som implementerar den generiska System.Collections.Generic.IEnumerable<'T>, som innehåller matriser, listor, uppsättningar och kartor i F#, och även de flesta .NET-samlingstyper, är kompatibla med seq typen och kan användas varhelst en sekvens förväntas.

Modulfunktioner

Seq-moduleni namnområdet FSharp.Collections innehåller funktioner för att arbeta med sekvenser. Dessa funktioner fungerar även med listor, matriser, kartor och uppsättningar, eftersom alla dessa typer kan räknas upp och därför kan behandlas som sekvenser.

Skapa sekvenser

Du kan skapa sekvenser med hjälp av sekvensuttryck, enligt beskrivningen tidigare, eller med hjälp av vissa funktioner.

Du kan skapa en tom sekvens med hjälp av Seq.empty, eller så kan du skapa en sekvens med bara ett angivet element med hjälp av Seq.singleton.

let seqEmpty = Seq.empty
let seqOne = Seq.singleton 10

Du kan använda Seq.init för att skapa en sekvens som elementen skapas för med hjälp av en funktion som du anger. Du anger också en storlek för sekvensen. Den här funktionen är precis som List.init, förutom att elementen inte skapas förrän du itererar genom sekvensen. Följande kod illustrerar användningen av Seq.init.

let seqFirst5MultiplesOf10 = Seq.init 5 (fun n -> n * 10)
Seq.iter (fun elem -> printf "%d " elem) seqFirst5MultiplesOf10

Utdata är

0 10 20 30 40

Genom att använda Seq.ofArray och Seq.ofList'T>< Function kan du skapa sekvenser från matriser och listor. Du kan dock också konvertera matriser och listor till sekvenser med hjälp av en gjuten operator. Båda teknikerna visas i följande kod.

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

Genom att använda Seq.cast kan du skapa en sekvens från en svagt typad samling, till exempel de som definieras i System.Collections. Sådana svagt skrivna samlingar har elementtypen System.Object och räknas upp med hjälp av den icke-generiska System.Collections.Generic.IEnumerable&#96;1 typen. Följande kod illustrerar användningen av Seq.cast för att konvertera en System.Collections.ArrayList till en sekvens.

open System

let arr = ResizeArray<int>(10)

for i in 1 .. 10 do
    arr.Add(10)

let seqCast = Seq.cast arr

Du kan definiera oändliga sekvenser med hjälp av funktionen Seq.initInfinite . För en sådan sekvens anger du en funktion som genererar varje element från elementets index. Oändliga sekvenser är möjliga på grund av lat utvärdering; element skapas efter behov genom att anropa den funktion som du anger. I följande kodexempel skapas en oändlig sekvens med flyttalsnummer, i det här fallet den alternerande serien med ömsesidiga kvadrater med efterföljande heltal.

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 genererar en sekvens från en beräkningsfunktion som tar ett tillstånd och transformerar den för att producera varje efterföljande element i sekvensen. Tillståndet är bara ett värde som används för att beräkna varje element och kan ändras när varje element beräknas. Det andra argumentet till Seq.unfold är det initiala värdet som används för att starta sekvensen. Seq.unfold använder en alternativtyp för tillståndet, vilket gör att du kan avsluta sekvensen genom att None returnera värdet. Följande kod visar två exempel på sekvenser och seq1fib, som genereras av en unfold åtgärd. Den första, seq1, är bara en enkel sekvens med tal upp till 20. Den andra, fib, använder unfold för att beräkna Fibonacci-sekvensen. Eftersom varje element i Fibonacci-sekvensen är summan av de två föregående Fibonacci-talen är tillståndsvärdet en tuppeln som består av de två föregående talen i sekvensen. Det initiala värdet är (0,1), de två första talen i sekvensen.

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

Utdata är följande:

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 

Följande kod är ett exempel som använder många av de sekvensmodulfunktioner som beskrivs här för att generera och beräkna värdena för oändliga sekvenser. Det kan ta några minuter att köra koden.

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

Söka efter element

Sekvenser stöder funktioner som är tillgängliga med listor: Seq.exists, Seq.exists2, Seq.find, Seq.findIndex, Seq.pick, Seq.tryFind och Seq.tryFindIndex. De versioner av dessa funktioner som är tillgängliga för sekvenser utvärderar sekvensen endast upp till det element som söks efter. Exempel finns i Listor.

Hämta underfrågor

Seq.filter och Seq.choose är som motsvarande funktioner som är tillgängliga för listor, förutom att filtreringen och valet inte sker förrän sekvenselementen utvärderas.

Seq.truncate skapar en sekvens från en annan sekvens, men begränsar sekvensen till ett angivet antal element. Seq.take skapar en ny sekvens som endast innehåller ett angivet antal element från början av en sekvens. Om det finns färre element i sekvensen än du anger att ta, Seq.take genererar en System.InvalidOperationException. Skillnaden mellan Seq.take och Seq.truncate är att Seq.truncate det inte ger upphov till ett fel om antalet element är färre än det tal som du anger.

Följande kod visar beteendet för och skillnaderna mellan Seq.truncate och 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

Utdata, innan felet inträffar, är följande.

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

Genom att använda Seq.takeWhile kan du ange en predikatfunktion (en boolesk funktion) och skapa en sekvens från en annan sekvens som består av de element i den ursprungliga sekvensen där predikatet är true, men stoppa före det första element som predikatet returnerar false. Seq.skip returnerar en sekvens som hoppar över ett angivet antal av de första elementen i en annan sekvens och returnerar de återstående elementen. Seq.skipWhile returnerar en sekvens som hoppar över de första elementen i en annan sekvens så länge predikatet returnerar trueoch sedan returnerar de återstående elementen, från och med det första elementet som predikatet returnerar false.

Följande kodexempel illustrerar beteendet för och skillnaderna mellan Seq.takeWhile, Seq.skipoch 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

Utdata är följande.

1 4 9
36 49 64 81 100
16 25 36 49 64 81 100

Transformera sekvenser

Seq.pairwise skapar en ny sekvens där efterföljande element i indatasekvensen grupperas i tupplar.

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 är som Seq.pairwise, förutom att i stället för att producera en sekvens med tupplar, genererar den en sekvens med matriser som innehåller kopior av intilliggande element (ett fönster) från sekvensen. Du anger antalet intilliggande element som du vill använda i varje matris.

I följande kodexempel visas användningen av Seq.windowed. I det här fallet är antalet element i fönstret 3. Exemplet använder printSeq, som definieras i föregående kodexempel.

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

Utdata är följande.

Inledande sekvens:

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

Åtgärder med flera sekvenser

Seq.zip och Seq.zip3 tar två eller tre sekvenser och producerar en sekvens av tupplar. Dessa funktioner liknar motsvarande funktioner som är tillgängliga för listor. Det finns ingen motsvarande funktion för att dela upp en sekvens i två eller flera sekvenser. Om du behöver den här funktionen för en sekvens konverterar du sekvensen till en lista och använder List.unzip.

Sortering, jämförelse och gruppering

Sorteringsfunktionerna som stöds för listor fungerar också med sekvenser. Detta inkluderar Seq.sort och Seq.sortBy. Dessa funktioner itererar genom hela sekvensen.

Du jämför två sekvenser med hjälp av funktionen Seq.compareWith . Funktionen jämför efterföljande element i tur och ordning och slutar när den stöter på det första ojämlika paret. Eventuella ytterligare element bidrar inte till jämförelsen.

Följande kod visar användningen av 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.")

I den föregående koden beräknas och granskas endast det första elementet och resultatet är -1.

Seq.countBy tar en funktion som genererar ett värde som kallas en nyckel för varje element. En nyckel genereras för varje element genom att anropa den här funktionen på varje element. Seq.countBy returnerar sedan en sekvens som innehåller nyckelvärdena och antalet element som genererade varje värde för nyckeln.

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

Utdata är följande.

(1, 34) (2, 33) (0, 33)

Föregående utdata visar att det fanns 34 element i den ursprungliga sekvensen som producerade nyckel 1, 33 värden som producerade nyckeln 2 och 33 värden som producerade nyckeln 0.

Du kan gruppera element i en sekvens genom att anropa Seq.groupBy. Seq.groupBy tar en sekvens och en funktion som genererar en nyckel från ett element. Funktionen körs på varje element i sekvensen. Seq.groupBy returnerar en sekvens med tupplar, där det första elementet i varje tupplar är nyckeln och det andra är en sekvens med element som producerar den nyckeln.

I följande kodexempel visas användningen av Seq.groupBy för att partitionera sekvensen med tal från 1 till 100 i tre grupper som har de distinkta nyckelvärdena 0, 1 och 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

Utdata är följande.

(1, seq [1; 4; 7; 10; ...]) (2, seq [2; 5; 8; 11; ...]) (0, seq [3; 6; 9; 12; ...])

Du kan skapa en sekvens som eliminerar duplicerade element genom att anropa Seq.distinct. Du kan också använda Seq.distinctBy, som kräver att en nyckelgenererande funktion anropas för varje element. Den resulterande sekvensen innehåller element i den ursprungliga sekvensen som har unika nycklar. senare element som skapar en dubblettnyckel till ett tidigare element ignoreras.

Följande kodexempel illustrerar användningen av Seq.distinct. Seq.distinct visas genom att generera sekvenser som representerar binära tal och sedan visa att de enda distinkta elementen är 0 och 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

Följande kod visar Seq.distinctBy genom att börja med en sekvens som innehåller negativa och positiva tal och använder funktionen absolut värde som nyckelgenererande funktion. Den resulterande sekvensen saknar alla positiva tal som motsvarar de negativa talen i sekvensen, eftersom de negativa talen visas tidigare i sekvensen och därför väljs i stället för de positiva tal som har samma absoluta värde eller nyckel.

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

Skrivskyddade och cachelagrade sekvenser

Seq.readonly skapar en skrivskyddad kopia av en sekvens. Seq.readonly är användbart när du har en skrivskyddad samling, till exempel en matris, och du inte vill ändra den ursprungliga samlingen. Den här funktionen kan användas för att bevara inkapsling av data. I följande kodexempel skapas en typ som innehåller en matris. En egenskap exponerar matrisen, men i stället för att returnera en matris returnerar den en sekvens som skapas från matrisen med hjälp Seq.readonlyav .

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 skapar en lagrad version av en sekvens. Använd Seq.cache för att undvika omvärdering av en sekvens, eller när du har flera trådar som använder en sekvens, men du måste se till att varje element endast utförs en gång. När du har en sekvens som används av flera trådar kan du ha en tråd som räknar upp och beräknar värdena för den ursprungliga sekvensen, och återstående trådar kan använda den cachelagrade sekvensen.

Utföra beräkningar på sekvenser

Enkla aritmetiska åtgärder liknar dem i listor, till exempel Seq.average, Seq.sum, Seq.averageBy, Seq.sumBy och så vidare.

Seq.fold, Seq.reduce och Seq.scan är som motsvarande funktioner som är tillgängliga för listor. Sekvenser stöder en delmängd av de fullständiga varianterna av dessa funktioner som listar stöd för. Mer information och exempel finns i Listor.

Se även