Pole (F#)

Pole jsou pevná, nulová, proměnlivá kolekce po sobě jdoucích datových prvků, které jsou všechny stejného typu.

Vytváření polí

Pole můžete vytvářet několika způsoby. Malé pole můžete vytvořit výpisem po sobě jdoucích hodnot a [||] oddělených středníky, jak je znázorněno v následujících příkladech.

let array1 = [| 1; 2; 3 |]

Každý prvek můžete také umístit na samostatný řádek, v takovém případě je oddělovač středníku volitelný.

let array1 =
    [|
        1
        2
        3
     |]

Typ prvků pole je odvozen z použitých literálů a musí být konzistentní.

// This is an array of 3 integers.
let array1 = [| 1; 2; 3 |]
// This is an array of a tuple of 3 integers.
let array2 = [| 1, 2, 3 |]

Následující kód způsobí chybu, protože 3.0 je float a 1 a 2 jsou celá čísla.

// Causes an error. The 3.0 (float) cannot be converted to integer implicitly.
// let array3 = [| 1; 2; 3.0 |]

Následující kód také způsobí chybu, protože 1,2 je řazená kolekce členů a 3 je celé číslo.

// Causes an error too. The 3 (integer) cannot be converted to tuple implicitly.
// let array4 = [| 1, 2; 3 |]

K vytváření polí můžete také použít sekvenční výrazy. Následuje příklad, který vytvoří pole čtverců celých čísel od 1 do 10.

let array3 = [| for i in 1 .. 10 -> i * i |]

Chcete-li vytvořit pole, ve kterém jsou všechny prvky inicializovány na nulu, použijte Array.zeroCreate.

let arrayOfTenZeroes : int array = Array.zeroCreate 10

Přístupové prvky

K prvkům pole můžete přistupovat pomocí hranatých závorek ([ a ]). Původní syntaxe tečky (.[index]) se stále podporuje, ale už se nedoporučuje jako F# 6.0.

array1[0]

Indexy polí začínají na 0.

K prvkům pole můžete přistupovat také pomocí zápisu řezu, který umožňuje zadat podadresa pole. Příklady zápisu řezu následují.

// Accesses elements from 0 to 2.

array1[0..2]

// Accesses elements from the beginning of the array to 2.

array1[..2]

// Accesses elements from 2 to the end of the array.

array1[2..]

Při použití zápisu řezu se vytvoří nová kopie pole.

Typy polí a moduly

Typ všech polí jazyka F# je typ System.Arrayrozhraní .NET Framework . Proto pole jazyka F# podporují všechny funkce, které jsou k dispozici v System.Array.

Modul Array podporuje operace s jednorozměrnými poli. Moduly Array2D, Array3Da Array4D obsahují funkce, které podporují operace s poli dvou, tří a čtyř dimenzí, v uvedeném pořadí. Pole pořadí větší než čtyři můžete vytvořit pomocí .System.Array

Jednoduché funkce

Array.get získá prvek. Array.length poskytuje délku pole. Array.set nastaví prvek na zadanou hodnotu. Následující příklad kódu znázorňuje použití těchto funkcí.

let array1 = Array.create 10 ""
for i in 0 .. array1.Length - 1 do
    Array.set array1 i (i.ToString())
for i in 0 .. array1.Length - 1 do
    printf "%s " (Array.get array1 i)

Výstup je následující.

0 1 2 3 4 5 6 7 8 9

Funkce, které vytvářejí pole

Několik funkcí vytváří pole bez nutnosti existujícího pole. Array.empty vytvoří nové pole, které neobsahuje žádné prvky. Array.create vytvoří pole zadané velikosti a nastaví všechny prvky na zadané hodnoty. Array.init vytvoří pole s ohledem na dimenzi a funkci pro vygenerování prvků. Array.zeroCreate vytvoří matici, ve které jsou všechny prvky inicializovány na nulovou hodnotu pro typ pole. Následující kód ukazuje tyto funkce.

let myEmptyArray = Array.empty
printfn "Length of empty array: %d" myEmptyArray.Length



printfn "Array of floats set to 5.0: %A" (Array.create 10 5.0)


printfn "Array of squares: %A" (Array.init 10 (fun index -> index * index))

let (myZeroArray : float array) = Array.zeroCreate 10

Výstup je následující.

Length of empty array: 0
Area of floats set to 5.0: [|5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0|]
Array of squares: [|0; 1; 4; 9; 16; 25; 36; 49; 64; 81|]

Array.copy vytvoří novou matici obsahující prvky, které se zkopírují z existujícího pole. Všimněte si, že kopie je mělká kopie, což znamená, že pokud je typ prvku odkazem, zkopíruje se pouze odkaz, nikoli podkladový objekt. Následující příklad kódu to dokládá.

open System.Text

let firstArray : StringBuilder array = Array.init 3 (fun index -> new StringBuilder(""))
let secondArray = Array.copy firstArray
// Reset an element of the first array to a new value.
firstArray[0] <- new StringBuilder("Test1")
// Change an element of the first array.
firstArray[1].Insert(0, "Test2") |> ignore
printfn "%A" firstArray
printfn "%A" secondArray

Výstup předchozího kódu je následující:

[|Test1; Test2; |]
[|; Test2; |]

Řetězec Test1 se zobrazí pouze v prvním poli, protože operace vytvoření nového prvku přepíše odkaz v firstArray , ale nemá vliv na původní odkaz na prázdný řetězec, který je stále přítomn secondArray. Řetězec Test2 se zobrazí v obou polích, protože Insert operace typu System.Text.StringBuilder má vliv na základní System.Text.StringBuilder objekt, na který se odkazuje v obou polích.

Array.sub vygeneruje nové pole z poduspořádku pole. Rozsah zadáte zadáním počátečního indexu a délky. Následující kód ukazuje použití Array.sub.

let a1 = [| 0 .. 99 |]
let a2 = Array.sub a1 5 10
printfn "%A" a2

Výstup ukazuje, že dílčí pole začíná na elementu 5 a obsahuje 10 prvků.

[|5; 6; 7; 8; 9; 10; 11; 12; 13; 14|]

Array.append vytvoří novou matici zkombinováním dvou existujících polí.

Následující kód ukazuje Array.append.

printfn "%A" (Array.append [| 1; 2; 3|] [| 4; 5; 6|])

Výstup předchozího kódu je následující.

[|1; 2; 3; 4; 5; 6|]

Array.choose vybere prvky pole, které chcete zahrnout do nového pole. Následující kód ukazuje Array.choose. Všimněte si, že typ prvku pole nemusí odpovídat typu hodnoty vrácené v typu možnosti. V tomto příkladu je int typ prvku a možnost je výsledkem polynomické funkce, elem*elem - 1jako číslo s plovoucí desetinou čárkou.

printfn "%A" (Array.choose (fun elem -> if elem % 2 = 0 then
                                            Some(float (elem*elem - 1))
                                        else
                                            None) [| 1 .. 10 |])

Výstup předchozího kódu je následující.

[|3.0; 15.0; 35.0; 63.0; 99.0|]

Array.collect spustí zadanou funkci na každém prvku pole existujícího pole a pak shromáždí prvky generované funkcí a zkombinuje je do nového pole. Následující kód ukazuje Array.collect.

printfn "%A" (Array.collect (fun elem -> [| 0 .. elem |]) [| 1; 5; 10|])

Výstup předchozího kódu je následující.

[|0; 1; 0; 1; 2; 3; 4; 5; 0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]

Array.concat přebírá posloupnost polí a kombinuje je do jednoho pole. Následující kód ukazuje Array.concat.

Array.concat [ [|0..3|] ; [|4|] ]
//output [|0; 1; 2; 3; 4|]

Array.concat [| [|0..3|] ; [|4|] |]
//output [|0; 1; 2; 3; 4|]

Array.filter vezme logickou funkci podmínky a vygeneruje nové pole, které obsahuje pouze ty prvky ze vstupního pole, pro které je podmínka pravdivá. Následující kód ukazuje Array.filter.

printfn "%A" (Array.filter (fun elem -> elem % 2 = 0) [| 1 .. 10|])

Výstup předchozího kódu je následující.

[|2; 4; 6; 8; 10|]

Array.rev vygeneruje nové pole vrácením pořadí existujícího pole. Následující kód ukazuje Array.rev.

let stringReverse (s: string) =
    System.String(Array.rev (s.ToCharArray()))

printfn "%A" (stringReverse("!dlrow olleH"))

Výstup předchozího kódu je následující.

"Hello world!"

Funkce v modulu pole, které transformují pole, můžete snadno kombinovat pomocí operátoru kanálu (|>), jak je znázorněno v následujícím příkladu.

[| 1 .. 10 |]
|> Array.filter (fun elem -> elem % 2 = 0)
|> Array.choose (fun elem -> if (elem <> 8) then Some(elem*elem) else None)
|> Array.rev
|> printfn "%A"

Výstup je

[|100; 36; 16; 4|]

Multidimenzionální pole

Multidimenzionální pole lze vytvořit, ale neexistuje žádná syntaxe pro zápis literálu multidimenzionálního pole. Pomocí operátoru array2D vytvořte pole z posloupnosti sekvencí prvků pole. Sekvence můžou být maticové nebo listové literály. Například následující kód vytvoří dvojrozměrné pole.

let my2DArray = array2D [ [ 1; 0]; [0; 1] ]

Pomocí funkce Array2D.init můžete také inicializovat pole dvou dimenzí a podobné funkce jsou k dispozici pro pole se třemi a čtyřmi dimenzemi. Tyto funkce používají funkci, která se používá k vytvoření prvků. Chcete-li vytvořit dvojrozměrné pole, které obsahuje prvky nastavené na počáteční hodnotu místo určení funkce, použijte Array2D.create funkci, která je také k dispozici pro pole až čtyři dimenze. Následující příklad kódu nejprve ukazuje, jak vytvořit pole polí, které obsahují požadované prvky, a pak používá Array2D.init k vygenerování požadované dvojrozměrné pole.

let arrayOfArrays = [| [| 1.0; 0.0 |]; [|0.0; 1.0 |] |]
let twoDimensionalArray = Array2D.init 2 2 (fun i j -> arrayOfArrays[i][j])

Syntaxe indexování a řezů polí je podporovaná pro pole až do pořadí 4. Když zadáte index ve více dimenzích, použijete čárky k oddělení indexů, jak je znázorněno v následujícím příkladu kódu.

twoDimensionalArray[0, 1] <- 1.0

Typ dvojrozměrného pole je napsán jako <type>[,] (například int[,], ), double[,]a typ trojrozměrného pole je napsán jako <type>[,,]a tak dále pro pole vyšších dimenzí.

Pro multidimenzionální pole je k dispozici pouze podmnožina funkcí dostupných pro jednorozměrná pole.

Vytváření řezů polí a multidimenzionální pole

V dvojrozměrném poli (matici) můžete extrahovat dílčí matici zadáním rozsahů a použitím zástupného znaku (*) k určení celých řádků nebo sloupců.

// Get rows 1 to N from an NxM matrix (returns a matrix):
matrix[1.., *]

// Get rows 1 to 3 from a matrix (returns a matrix):
matrix[1..3, *]

// Get columns 1 to 3 from a matrix (returns a matrix):
matrix[*, 1..3]

// Get a 3x3 submatrix:
matrix[1..3, 1..3]

Multidimenzionální pole můžete rozložit do dílčích polí stejné nebo nižší dimenze. Vektor můžete například získat z matice zadáním jednoho řádku nebo sloupce.

// Get row 3 from a matrix as a vector:
matrix[3, *]

// Get column 3 from a matrix as a vector:
matrix[*, 3]

Tuto syntaxi řezů můžete použít pro typy, které implementují operátory přístupu elementu a přetížené GetSlice metody. Následující kód například vytvoří typ Matice, který zabalí pole F# 2D, implementuje vlastnost Item, která poskytuje podporu indexování pole a implementuje tři verze GetSlice. Pokud tento kód můžete použít jako šablonu pro typy matic, můžete použít všechny operace řezů, které tato část popisuje.

type Matrix<'T>(N: int, M: int) =
    let internalArray = Array2D.zeroCreate<'T> N M

    member this.Item
        with get(a: int, b: int) = internalArray[a, b]
        and set(a: int, b: int) (value:'T) = internalArray[a, b] <- value

    member this.GetSlice(rowStart: int option, rowFinish : int option, colStart: int option, colFinish : int option) =
        let rowStart =
            match rowStart with
            | Some(v) -> v
            | None -> 0
        let rowFinish =
            match rowFinish with
            | Some(v) -> v
            | None -> internalArray.GetLength(0) - 1
        let colStart =
            match colStart with
            | Some(v) -> v
            | None -> 0
        let colFinish =
            match colFinish with
            | Some(v) -> v
            | None -> internalArray.GetLength(1) - 1
        internalArray[rowStart..rowFinish, colStart..colFinish]

    member this.GetSlice(row: int, colStart: int option, colFinish: int option) =
        let colStart =
            match colStart with
            | Some(v) -> v
            | None -> 0
        let colFinish =
            match colFinish with
            | Some(v) -> v
            | None -> internalArray.GetLength(1) - 1
        internalArray[row, colStart..colFinish]

    member this.GetSlice(rowStart: int option, rowFinish: int option, col: int) =
        let rowStart =
            match rowStart with
            | Some(v) -> v
            | None -> 0
        let rowFinish =
            match rowFinish with
            | Some(v) -> v
            | None -> internalArray.GetLength(0) - 1
        internalArray[rowStart..rowFinish, col]

module test =
    let generateTestMatrix x y =
        let matrix = new Matrix<float>(3, 3)
        for i in 0..2 do
            for j in 0..2 do
                matrix[i, j] <- float(i) * x - float(j) * y
        matrix

    let test1 = generateTestMatrix 2.3 1.1
    let submatrix = test1[0..1, 0..1]
    printfn $"{submatrix}"

    let firstRow = test1[0,*]
    let secondRow = test1[1,*]
    let firstCol = test1[*,0]
    printfn $"{firstCol}"

Logické funkce v polích

Funkce Array.exists a Array.exists2 testovací prvky v jednom nebo dvou polích. Tyto funkce přebírají testovací funkci a vrátí true se, pokud existuje prvek (nebo dvojice elementů), Array.exists2který splňuje podmínku.

Následující kód ukazuje použití Array.exists a Array.exists2. V těchto příkladech jsou nové funkce vytvořeny použitím pouze jednoho z argumentů, v těchto případech argument funkce.

let allNegative = Array.exists (fun elem -> abs (elem) = elem) >> not
printfn "%A" (allNegative [| -1; -2; -3 |])
printfn "%A" (allNegative [| -10; -1; 5 |])
printfn "%A" (allNegative [| 0 |])


let haveEqualElement = Array.exists2 (fun elem1 elem2 -> elem1 = elem2)
printfn "%A" (haveEqualElement [| 1; 2; 3 |] [| 3; 2; 1|])

Výstup předchozího kódu je následující.

true
false
false
true

Podobně funkce Array.forall testuje pole, aby určila, zda každý prvek splňuje logickou podmínku. Varianta Array.forall2 dělá totéž pomocí logické funkce, která zahrnuje prvky dvou polí se stejnou délkou. Použití těchto funkcí znázorňuje následující kód.

let allPositive = Array.forall (fun elem -> elem > 0)
printfn "%A" (allPositive [| 0; 1; 2; 3 |])
printfn "%A" (allPositive [| 1; 2; 3 |])


let allEqual = Array.forall2 (fun elem1 elem2 -> elem1 = elem2)
printfn "%A" (allEqual [| 1; 2 |] [| 1; 2 |])
printfn "%A" (allEqual [| 1; 2 |] [| 2; 1 |])

Výstup pro tyto příklady je následující.

false
true
true
false

Vyhledávací pole

Array.find vezme logickou funkci a vrátí první prvek, pro který funkce vrátí true, nebo vyvolá System.Collections.Generic.KeyNotFoundException , pokud není nalezen žádný prvek, který splňuje podmínku. Array.findIndex je jako Array.find, s tím rozdílem, že vrátí index prvku místo samotného prvku.

Následující kód používá Array.find a Array.findIndex k vyhledání čísla, které je perfektní čtverec i dokonalá datová krychle.

let arrayA = [| 2 .. 100 |]
let delta = 1.0e-10
let isPerfectSquare (x:int) =
    let y = sqrt (float x)
    abs(y - round y) < delta
let isPerfectCube (x:int) =
    let y = System.Math.Pow(float x, 1.0/3.0)
    abs(y - round y) < delta
let element = Array.find (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
let index = Array.findIndex (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
printfn "The first element that is both a square and a cube is %d and its index is %d." element index

Výstup je následující.

The first element that is both a square and a cube is 64 and its index is 62.

Array.tryFind je jako Array.find, s tím rozdílem, že jeho výsledek je typ možnosti a vrátí None , pokud nebyl nalezen žádný prvek. Array.tryFind místo Array.find toho, když nevíte, zda je odpovídající prvek v poli. Podobně se podobá Array.findIndex tomu, Array.tryFindIndex že typ možnosti je návratová hodnota. Pokud nebyl nalezen žádný prvek, je tato možnost None.

Následující kód ukazuje použití Array.tryFind. Tento kód závisí na předchozím kódu.

let delta = 1.0e-10
let isPerfectSquare (x:int) =
    let y = sqrt (float x)
    abs(y - round y) < delta
let isPerfectCube (x:int) =
    let y = System.Math.Pow(float x, 1.0/3.0)
    abs(y - round y) < delta
let lookForCubeAndSquare array1 =
    let result = Array.tryFind (fun elem -> isPerfectSquare elem && isPerfectCube elem) array1
    match result with
    | Some x -> printfn "Found an element: %d" x
    | None -> printfn "Failed to find a matching element."

lookForCubeAndSquare [| 1 .. 10 |]
lookForCubeAndSquare [| 100 .. 1000 |]
lookForCubeAndSquare [| 2 .. 50 |]

Výstup je následující.

Found an element: 1
Found an element: 729
Failed to find a matching element.

Použijte Array.tryPick , když potřebujete kromě jeho hledání transformovat prvek. Výsledkem je první prvek, pro který funkce vrátí transformovaný prvek jako hodnotu možnosti, nebo None pokud se takový prvek nenajde.

Následující kód ukazuje použití Array.tryPick. V tomto případě je místo výrazu lambda definováno několik místních pomocných funkcí pro zjednodušení kódu.

let findPerfectSquareAndCube array1 =
    let delta = 1.0e-10
    let isPerfectSquare (x:int) =
        let y = sqrt (float x)
        abs(y - round y) < delta
    let isPerfectCube (x:int) =
        let y = System.Math.Pow(float x, 1.0/3.0)
        abs(y - round y) < delta
    // intFunction : (float -> float) -> int -> int
    // Allows the use of a floating point function with integers.
    let intFunction function1 number = int (round (function1 (float number)))
    let cubeRoot x = System.Math.Pow(x, 1.0/3.0)
    // testElement: int -> (int * int * int) option
    // Test an element to see whether it is a perfect square and a perfect
    // cube, and, if so, return the element, square root, and cube root
    // as an option value. Otherwise, return None.
    let testElement elem =
        if isPerfectSquare elem && isPerfectCube elem then
            Some(elem, intFunction sqrt elem, intFunction cubeRoot elem)
        else None
    match Array.tryPick testElement array1 with
    | Some (n, sqrt, cuberoot) -> printfn "Found an element %d with square root %d and cube root %d." n sqrt cuberoot
    | None -> printfn "Did not find an element that is both a perfect square and a perfect cube."

findPerfectSquareAndCube [| 1 .. 10 |]
findPerfectSquareAndCube [| 2 .. 100 |]
findPerfectSquareAndCube [| 100 .. 1000 |]
findPerfectSquareAndCube [| 1000 .. 10000 |]
findPerfectSquareAndCube [| 2 .. 50 |]

Výstup je následující.

Found an element 1 with square root 1 and cube root 1.
Found an element 64 with square root 8 and cube root 4.
Found an element 729 with square root 27 and cube root 9.
Found an element 4096 with square root 64 and cube root 16.
Did not find an element that is both a perfect square and a perfect cube.

Provádění výpočtů v polích

Funkce Array.average vrátí průměr každého prvku v matici. Je omezena na typy prvků, které podporují přesné dělení celočíselným číslem, které zahrnuje typy s plovoucí desetinou čárkou, ale ne celočíselné typy. Funkce Array.averageBy vrátí průměr výsledků volání funkce na každém prvku. Pro matici celočíselného typu můžete použít Array.averageBy a nechat funkci převést každý prvek na typ s plovoucí desetinou čárkou pro výpočet.

Pokud typ elementu podporuje, použijte Array.max nebo Array.min získáte maximální nebo minimální prvek. Podobně a Array.minBy povolit, Array.maxBy aby se funkce nejprve spustila, možná transformovat na typ, který podporuje porovnání.

Array.sum přidá prvky pole a Array.sumBy zavolá funkci pro každý prvek a výsledky sečte dohromady.

Chcete-li spustit funkci pro každý prvek v poli bez uložení vrácených hodnot, použijte Array.iter. Pro funkci zahrnující dvě matice se stejnou délkou použijte Array.iter2. Pokud potřebujete také zachovat pole výsledků funkce, použít Array.map nebo Array.map2, která pracuje se dvěma poli najednou.

Varianty Array.iteri a Array.iteri2 umožňují index prvku být zapojen do výpočtu; totéž platí pro Array.mapi a Array.mapi2.

Funkce Array.fold, , Array.foldBackArray.reduce, Array.reduceBack, Array.scana Array.scanBack spouštění algoritmů, které zahrnují všechny prvky pole. Podobně varianty Array.fold2 a Array.foldBack2 výpočty provádějí na dvou polích.

Tyto funkce pro provádění výpočtů odpovídají funkcím stejného názvu v modulu List. Příklady použití najdete v tématu Seznamy.

Úprava polí

Array.set nastaví prvek na zadanou hodnotu. Array.fill nastaví rozsah prvků v matici na zadanou hodnotu. Následující kód poskytuje příklad .Array.fill

let arrayFill1 = [| 1 .. 25 |]
Array.fill arrayFill1 2 20 0
printfn "%A" arrayFill1

Výstup je následující.

[|1; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 23; 24; 25|]

Můžete použít Array.blit ke zkopírování pododdílu jednoho pole do jiného pole.

Převod na jiné typy a z jiných typů

Array.ofList vytvoří pole ze seznamu. Array.ofSeq vytvoří pole z sekvence. Array.toList a Array.toSeq převeďte na tyto další typy kolekcí z typu pole.

Řazení polí

Slouží Array.sort k seřazení pole pomocí obecné porovnávací funkce. Slouží Array.sortBy k určení funkce, která vygeneruje hodnotu, která se označuje jako klíč, k řazení pomocí obecné porovnávací funkce na klíči. Použijte Array.sortWith , pokud chcete zadat vlastní funkci porovnání. Array.sort, Array.sortBya Array.sortWith všechny vrátí seřazené pole jako novou matici. Varianty Array.sortInPlacea Array.sortInPlaceByArray.sortInPlaceWith upravte existující pole namísto vrácení nového pole.

Pole a řazené kolekce členů

Funkce Array.zip a Array.unzip převod polí párů řazené kolekce členů na řazené kolekce členů a naopak. Array.zip3 a Array.unzip3 jsou podobné s tím rozdílem, že pracují s řazenými kolekcemi členů tří prvků nebo řazených kolekcí členů tří polí.

Paralelní výpočty v polích

Modul Array.Parallel obsahuje funkce pro provádění paralelních výpočtů v polích. Tento modul není k dispozici v aplikacích, které cílí na verze .NET Framework před verzí 4.

Viz také