Matrici (F#)

Le matrici sono raccolte a dimensione fissa, in base zero e modificabili di elementi dati consecutivi che sono tutti dello stesso tipo.

Creare matrici

È possibile creare matrici in diversi modi. È possibile creare una matrice di piccole dimensioni elencando i valori consecutivi tra [| e separati da punti e |] virgola, come illustrato negli esempi seguenti.

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

È anche possibile inserire ogni elemento su una riga separata, nel qual caso il separatore di punto e virgola è facoltativo.

let array1 =
    [|
        1
        2
        3
     |]

Il tipo degli elementi della matrice viene dedotto dai valori letterali usati e deve essere coerente.

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

Il codice seguente genera un errore perché 3.0 è float e 1 e 2 sono numeri interi.

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

Il codice seguente causa anche un errore perché 1,2 è una tupla e 3 è un numero intero.

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

È anche possibile usare espressioni di sequenza per creare matrici. Di seguito è riportato un esempio che crea una matrice di quadrati di interi da 1 a 10.

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

Per creare una matrice in cui tutti gli elementi vengono inizializzati su zero, usare Array.zeroCreate.

let arrayOfTenZeroes : int array = Array.zeroCreate 10

Elementi di accesso

È possibile accedere agli elementi della matrice usando parentesi quadre ([ e ]). La sintassi del punto originale (.[index]) è ancora supportata, ma non è più consigliata a partire da F# 6.0.

array1[0]

Gli indici di matrice iniziano da 0.

È anche possibile accedere agli elementi della matrice usando la notazione slice, che consente di specificare un intervallo secondario della matrice. Di seguito sono riportati esempi di notazione di sezione.

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

Quando viene usata la notazione delle sezioni, viene creata una nuova copia della matrice.

Tipi e moduli di matrice

Il tipo di tutte le matrici F# è il tipo System.Array.NET Framework . Pertanto, le matrici F# supportano tutte le funzionalità disponibili in System.Array.

Il Array modulo supporta operazioni su matrici unidimensionali. I moduli Array2D, Array3De Array4D contengono funzioni che supportano le operazioni sulle matrici di due, tre e quattro dimensioni, rispettivamente. È possibile creare matrici di rango maggiore di quattro usando System.Array.

Funzioni semplici

Array.get ottiene un elemento . Array.length fornisce la lunghezza di una matrice. Array.set imposta un elemento su un valore specificato. Nell'esempio di codice seguente viene illustrato l'uso di queste funzioni.

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)

L'output è indicato di seguito.

0 1 2 3 4 5 6 7 8 9

Funzioni che creano matrici

Diverse funzioni creano matrici senza richiedere una matrice esistente. Array.empty crea una nuova matrice che non contiene elementi. Array.create crea una matrice di dimensioni specificate e imposta tutti gli elementi sui valori specificati. Array.init crea una matrice, data una dimensione e una funzione per generare gli elementi. Array.zeroCreate crea una matrice in cui tutti gli elementi vengono inizializzati sul valore zero per il tipo della matrice. Il codice seguente illustra queste funzioni.

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

L'output è indicato di seguito.

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 crea una nuova matrice che contiene elementi copiati da una matrice esistente. Si noti che la copia è una copia superficiale, ovvero se il tipo di elemento è un tipo riferimento, viene copiato solo il riferimento, non l'oggetto sottostante. Questo aspetto è illustrato nell'esempio di codice seguente.

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

L'output del codice precedente è il seguente:

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

La stringa Test1 viene visualizzata solo nella prima matrice perché l'operazione di creazione di un nuovo elemento sovrascrive il riferimento in firstArray ma non influisce sul riferimento originale a una stringa vuota ancora presente in secondArray. La stringa Test2 viene visualizzata in entrambe le matrici perché l'operazione Insert sul System.Text.StringBuilder tipo influisce sull'oggetto sottostante System.Text.StringBuilder , a cui viene fatto riferimento in entrambe le matrici.

Array.sub genera una nuova matrice da un sottoinsieme di una matrice. Specificare il sottorange specificando l'indice iniziale e la lunghezza. Il codice seguente illustra l'uso di Array.sub.

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

L'output mostra che la sottomaschera inizia all'elemento 5 e contiene 10 elementi.

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

Array.append crea una nuova matrice combinando due matrici esistenti.

Il codice seguente illustra Array.append.

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

L'output del codice precedente è il seguente.

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

Array.choose seleziona gli elementi di una matrice da includere in una nuova matrice. Il codice seguente illustra Array.choose. Si noti che il tipo di elemento della matrice non deve corrispondere al tipo del valore restituito nel tipo di opzione. In questo esempio il tipo di elemento è int e l'opzione è il risultato di una funzione polinomiale, elem*elem - 1, come numero a virgola mobile.

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

L'output del codice precedente è il seguente.

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

Array.collect esegue una funzione specificata su ogni elemento matrice di una matrice esistente e quindi raccoglie gli elementi generati dalla funzione e li combina in una nuova matrice. Il codice seguente illustra Array.collect.

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

L'output del codice precedente è il seguente.

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

Array.concat accetta una sequenza di matrici e le combina in una singola matrice. Il codice seguente illustra 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 accetta una funzione di condizione booleana e genera una nuova matrice che contiene solo gli elementi della matrice di input per cui la condizione è true. Il codice seguente illustra Array.filter.

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

L'output del codice precedente è il seguente.

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

Array.rev genera una nuova matrice invertendo l'ordine di una matrice esistente. Il codice seguente illustra Array.rev.

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

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

L'output del codice precedente è il seguente.

"Hello world!"

È possibile combinare facilmente le funzioni nel modulo array che trasformano le matrici usando l'operatore pipeline (|>), come illustrato nell'esempio seguente.

[| 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"

L'output è

[|100; 36; 16; 4|]

Matrici multidimensionali

È possibile creare una matrice multidimensionale, ma non esiste alcuna sintassi per la scrittura di un valore letterale di matrice multidimensionale. Usare l'operatore array2D per creare una matrice da una sequenza di sequenze di elementi della matrice. Le sequenze possono essere valori letterali di matrice o elenco. Ad esempio, il codice seguente crea una matrice bidimensionale.

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

È anche possibile usare la funzione Array2D.init per inizializzare matrici di due dimensioni e funzioni simili sono disponibili per le matrici di tre e quattro dimensioni. Queste funzioni accettano una funzione usata per creare gli elementi. Per creare una matrice bidimensionale contenente elementi impostati su un valore iniziale anziché specificare una funzione, usare la Array2D.create funzione , disponibile anche per le matrici fino a quattro dimensioni. Nell'esempio di codice seguente viene prima illustrato come creare una matrice di matrici che contengono gli elementi desiderati e quindi viene Array2D.init usata per generare la matrice bidimensionale desiderata.

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

L'indicizzazione e la sintassi di sezionamento delle matrici sono supportate per le matrici fino a 4. Quando si specifica un indice in più dimensioni, usare le virgole per separare gli indici, come illustrato nell'esempio di codice seguente.

twoDimensionalArray[0, 1] <- 1.0

Il tipo di una matrice bidimensionale viene scritto come <type>[,] (ad esempio, int[,], double[,]) e il tipo di una matrice tridimensionale viene scritto come <type>[,,]e così via per le matrici di dimensioni superiori.

Per le matrici multidimensionali è disponibile solo un subset delle funzioni disponibili per le matrici unidimensionali.

Sezionamento di matrici e matrici multidimensionali

In una matrice bidimensionale (matrice) è possibile estrarre una sottomatrici specificando intervalli e usando un carattere jolly (*) per specificare righe o colonne intere.

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

È possibile scomporre una matrice multidimensionale in sottoarray della stessa dimensione o inferiore. Ad esempio, è possibile ottenere un vettore da una matrice specificando una singola riga o colonna.

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

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

È possibile usare questa sintassi di sezionamento per i tipi che implementano gli operatori di accesso agli elementi e i metodi di GetSlice overload. Ad esempio, il codice seguente crea un tipo Matrix che esegue il wrapping della matrice F# 2D, implementa una proprietà Item per fornire il supporto per l'indicizzazione di matrici e implementa tre versioni di GetSlice. Se è possibile usare questo codice come modello per i tipi di matrice, è possibile usare tutte le operazioni di sezionamento descritte in questa sezione.

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}"

Funzioni booleane nelle matrici

Le funzioni Array.exists e Array.exists2 gli elementi di test in una o due matrici, rispettivamente. Queste funzioni accettano una funzione di test e restituiscono true se è presente un elemento (o una coppia di elementi per Array.exists2) che soddisfa la condizione.

Il codice seguente illustra l'uso di Array.exists e Array.exists2. In questi esempi vengono create nuove funzioni applicando solo uno degli argomenti, in questi casi l'argomento funzione .

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

L'output del codice precedente è il seguente.

true
false
false
true

Analogamente, la funzione Array.forall testa una matrice per determinare se ogni elemento soddisfa una condizione booleana. La variante Array.forall2 esegue la stessa operazione usando una funzione booleana che coinvolge elementi di due matrici di lunghezza uguale. Il codice seguente illustra l'uso di queste funzioni.

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

L'output per questi esempi è il seguente.

false
true
true
false

Matrici di ricerca

Array.find accetta una funzione booleana e restituisce il primo elemento per il quale la funzione restituisce trueo genera un valore se non viene trovato alcun System.Collections.Generic.KeyNotFoundException elemento che soddisfa la condizione. Array.findIndex è simile Array.finda , ad eccezione del fatto che restituisce l'indice dell'elemento anziché l'elemento stesso.

Il codice seguente usa Array.find e Array.findIndex per individuare un numero che sia un quadrato perfetto che un cubo perfetto.

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

L'output è indicato di seguito.

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

Array.tryFind è simile Array.finda , ad eccezione del fatto che il risultato è un tipo di opzione e restituisce None se non viene trovato alcun elemento. Array.tryFind deve essere usato invece di Array.find quando non si sa se un elemento corrispondente si trova nella matrice. Analogamente, Array.tryFindIndex è simile Array.findIndex al fatto che il tipo di opzione è il valore restituito. Se non viene trovato alcun elemento, l'opzione è None.

Il codice seguente illustra l'uso di Array.tryFind. Questo codice dipende dal codice precedente.

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

L'output è indicato di seguito.

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

Usare Array.tryPick quando è necessario trasformare un elemento oltre a trovarlo. Il risultato è il primo elemento per il quale la funzione restituisce l'elemento trasformato come valore di opzione o None se non viene trovato alcun elemento di questo tipo.

Il codice seguente illustra l'uso di Array.tryPick. In questo caso, invece di un'espressione lambda, vengono definite diverse funzioni helper locali per semplificare il codice.

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

L'output è indicato di seguito.

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.

Eseguire calcoli su matrici

La Array.average funzione restituisce la media di ogni elemento in una matrice. È limitato ai tipi di elemento che supportano la divisione esatta per un numero intero, che include tipi a virgola mobile, ma non tipi integrali. La Array.averageBy funzione restituisce la media dei risultati della chiamata di una funzione in ogni elemento. Per una matrice di tipo integrale, è possibile usare Array.averageBy e fare in modo che la funzione converta ogni elemento in un tipo a virgola mobile per il calcolo.

Usare Array.max o Array.min per ottenere l'elemento massimo o minimo, se il tipo di elemento lo supporta. Analogamente, Array.maxBy e Array.minBy consentire l'esecuzione di una funzione per prima, ad esempio per trasformare in un tipo che supporta il confronto.

Array.sum aggiunge gli elementi di una matrice e Array.sumBy chiama una funzione su ogni elemento e aggiunge i risultati insieme.

Per eseguire una funzione su ogni elemento di una matrice senza archiviare i valori restituiti, usare Array.iter. Per una funzione che include due matrici di lunghezza uguale, usare Array.iter2. Se è anche necessario mantenere una matrice dei risultati della funzione, usare Array.map o Array.map2, che opera su due matrici alla volta.

Le varianti Array.iteri e Array.iteri2 consentono l'indice dell'elemento da coinvolgere nel calcolo; lo stesso vale per Array.mapi e Array.mapi2.

Le funzioni Array.fold, Array.foldBackArray.scanArray.reduceArray.reduceBacke Array.scanBack eseguono algoritmi che coinvolgono tutti gli elementi di una matrice. Analogamente, le varianti Array.fold2 ed Array.foldBack2 eseguono calcoli su due matrici.

Queste funzioni per l'esecuzione di calcoli corrispondono alle funzioni con lo stesso nome nel modulo List. Per esempi di utilizzo, vedere Elenchi.

Modificare le matrici

Array.set imposta un elemento su un valore specificato. Array.fill imposta un intervallo di elementi in una matrice su un valore specificato. Il codice seguente fornisce un esempio di Array.fill.

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

L'output è indicato di seguito.

[|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|]

È possibile usare Array.blit per copiare una sottosezione di una matrice in un'altra matrice.

Converti in e da altri tipi

Array.ofList crea una matrice da un elenco. Array.ofSeq crea una matrice da una sequenza. Array.toList e Array.toSeq convertire in questi altri tipi di raccolta dal tipo di matrice.

Ordina matrici

Usare Array.sort per ordinare una matrice usando la funzione di confronto generica. Usare Array.sortBy per specificare una funzione che genera un valore, detto chiave, per ordinare usando la funzione di confronto generica sulla chiave. Usare Array.sortWith se si vuole fornire una funzione di confronto personalizzata. Array.sort, Array.sortBye Array.sortWith restituiscono la matrice ordinata come nuova matrice. Le varianti Array.sortInPlace, Array.sortInPlaceBye Array.sortInPlaceWith modificano la matrice esistente anziché restituirne una nuova.

Matrici e tuple

Le funzioni Array.zip e Array.unzip converte le matrici di coppie di tuple in tuple di matrici e viceversa. Array.zip3 e Array.unzip3 sono simili, ad eccezione del fatto che funzionano con tuple di tre elementi o tuple di tre matrici.

Calcoli paralleli su matrici

Il modulo Array.Parallel contiene funzioni per l'esecuzione di calcoli paralleli su matrici. Questo modulo non è disponibile nelle applicazioni destinate alle versioni di .NET Framework precedenti alla versione 4.

Vedi anche