Partilhar via


Matrizes (F#)

As matrizes são coleções mutáveis, baseadas em zero e de tamanho fixo de elementos de dados consecutivos que são todos do mesmo tipo.

Criar matrizes

Você pode criar matrizes de várias maneiras. Você pode criar uma pequena matriz listando valores consecutivos entre [| e |] e separados por ponto-e-vírgula, conforme mostrado nos exemplos a seguir.

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

Você também pode colocar cada elemento em uma linha separada, caso em que o separador ponto-e-vírgula é opcional.

let array1 =
    [|
        1
        2
        3
     |]

O tipo dos elementos da matriz é inferido a partir dos literais usados e deve ser consistente.

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

O código a seguir causa um erro porque 3.0 é um float e 1 e 2 são inteiros.

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

O código a seguir também causa um erro porque 1,2 é uma tupla e 3 é um inteiro.

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

Você também pode usar expressões de sequência para criar matrizes. A seguir está um exemplo que cria uma matriz de quadrados de inteiros de 1 a 10.

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

Para criar uma matriz na qual todos os elementos são inicializados como zero, use Array.zeroCreate.

let arrayOfTenZeroes : int array = Array.zeroCreate 10

Elementos de acesso

Você pode acessar elementos de matriz usando colchetes ([ e ]). A sintaxe de pontos original (.[index]) ainda é suportada, mas não é mais recomendada a partir do F# 6.0.

array1[0]

Os índices de matriz começam em 0.

Você também pode acessar elementos de matriz usando notação de fatia, que permite especificar um subintervalo da matriz. Seguem-se exemplos de notação de fatia.

// 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 a notação de fatia é usada, uma nova cópia da matriz é criada.

Tipos de matrizes e módulos

O tipo de todas as matrizes F# é o tipo System.Array.NET Framework . Portanto, as matrizes F# suportam todas as funcionalidades disponíveis no System.Array.

O Array módulo suporta operações em matrizes unidimensionais. Os módulos Array2D, Array3De Array4D contêm funções que suportam operações em matrizes de duas, três e quatro dimensões, respectivamente. Você pode criar matrizes de classificação maior que quatro usando System.Array.

Funções simples

Array.get obtém um elemento. Array.length Fornece o comprimento de uma matriz. Array.set Define um elemento como um valor especificado. O exemplo de código a seguir ilustra o uso dessas funções.

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)

A saída é a seguinte.

0 1 2 3 4 5 6 7 8 9

Funções que criam matrizes

Várias funções criam matrizes sem a necessidade de uma matriz existente. Array.empty Cria uma nova matriz que não contém nenhum elemento . Array.create Cria uma matriz de um tamanho especificado e define todos os elementos como valores fornecidos. Array.init cria uma matriz, dada uma dimensão e uma função para gerar os elementos. Array.zeroCreate Cria uma matriz na qual todos os elementos são inicializados com o valor zero para o tipo da matriz. O código a seguir demonstra essas funções.

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

A saída é a seguinte.

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 Cria uma nova matriz que contém elementos que são copiados de uma matriz existente. Observe que a cópia é uma cópia superficial, o que significa que, se o tipo de elemento for um tipo de referência, somente a referência será copiada, não o objeto subjacente. O exemplo de código a seguir ilustra isso.

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

A saída do código anterior é a seguinte:

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

A cadeia de caracteres Test1 aparece apenas na primeira matriz porque a operação de criação de um novo elemento substitui a referência em firstArray , mas não afeta a referência original para uma cadeia de caracteres vazia que ainda está presente no secondArray. A cadeia de caracteres Test2 aparece em ambas as matrizes porque a Insert operação no System.Text.StringBuilder tipo afeta o objeto subjacente System.Text.StringBuilder , que é referenciado em ambas as matrizes.

Array.sub gera uma nova matriz a partir de um subintervalo de uma matriz. Você especifica o subintervalo fornecendo o índice inicial e o comprimento. O código a seguir demonstra o uso do Array.sub.

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

A saída mostra que a submatriz começa no elemento 5 e contém 10 elementos.

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

Array.append Cria uma nova matriz combinando duas matrizes existentes.

O código a seguir demonstra Array.append.

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

A saída do código anterior é a seguinte.

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

Array.choose Seleciona elementos de uma matriz para incluir em uma nova matriz. O código a seguir demonstra Array.chooseo . Observe que o tipo de elemento da matriz não precisa corresponder ao tipo do valor retornado no tipo de opção. Neste exemplo, o tipo de elemento é int e a opção é o resultado de uma função polinomial, elem*elem - 1como um número de ponto flutuante.

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

A saída do código anterior é a seguinte.

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

Array.collect Executa uma função especificada em cada elemento de matriz de uma matriz existente e, em seguida, coleta os elementos gerados pela função e os combina em uma nova matriz. O código a seguir demonstra Array.collecto .

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

A saída do código anterior é a seguinte.

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

Array.concat pega uma sequência de matrizes e as combina em uma única matriz. O código a seguir demonstra Array.concato .

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

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

Array.filter usa uma função de condição booleana e gera uma nova matriz que contém apenas os elementos da matriz de entrada para a qual a condição é verdadeira. O código a seguir demonstra Array.filtero .

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

A saída do código anterior é a seguinte.

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

Array.rev gera uma nova matriz invertendo a ordem de uma matriz existente. O código a seguir demonstra Array.revo .

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

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

A saída do código anterior é a seguinte.

"Hello world!"

Você pode facilmente combinar funções no módulo de matriz que transformam matrizes usando o operador de pipeline (|>), conforme mostrado no exemplo a seguir.

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

A saída é

[|100; 36; 16; 4|]

Matrizes multidimensionais

Uma matriz multidimensional pode ser criada, mas não há sintaxe para escrever uma matriz multidimensional literal. Use o operador array2D para criar uma matriz a partir de uma sequência de sequências de elementos de matriz. As sequências podem ser literais de matriz ou lista. Por exemplo, o código a seguir cria uma matriz bidimensional.

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

Você também pode usar a função Array2D.init para inicializar matrizes de duas dimensões, e funções semelhantes estão disponíveis para matrizes de três e quatro dimensões. Essas funções assumem uma função que é usada para criar os elementos. Para criar uma matriz bidimensional que contenha elementos definidos como um valor inicial em vez de especificar uma função, use a Array2D.create função, que também está disponível para matrizes de até quatro dimensões. O exemplo de código a seguir mostra primeiro como criar uma matriz de matrizes que contêm os elementos desejados e, em seguida, usa Array2D.init para gerar a matriz bidimensional desejada.

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

A sintaxe de indexação e fatiamento de matrizes é suportada para matrizes até a classificação 4. Ao especificar um índice em várias dimensões, você usa vírgulas para separar os índices, conforme ilustrado no exemplo de código a seguir.

twoDimensionalArray[0, 1] <- 1.0

O tipo de uma matriz bidimensional é escrito como <type>[,] (por exemplo, int[,], double[,]), e o tipo de uma matriz tridimensional é escrito como <type>[,,], e assim por diante para matrizes de dimensões mais altas.

Apenas um subconjunto das funções disponíveis para matrizes unidimensionais também está disponível para matrizes multidimensionais.

Fatiamento de matrizes e matrizes multidimensionais

Em uma matriz bidimensional (uma matriz), você pode extrair uma submatriz especificando intervalos e usando um caractere curinga (*) para especificar linhas ou colunas inteiras.

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

Você pode decompor uma matriz multidimensional em submatrizes da mesma dimensão ou de menor dimensão. Por exemplo, você pode obter um vetor de uma matriz especificando uma única linha ou coluna.

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

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

Você pode usar essa sintaxe de fatiamento para tipos que implementam os operadores de acesso ao elemento e métodos sobrecarregados GetSlice . Por exemplo, o código a seguir cria um tipo Matrix que encapsula a matriz 2D F#, implementa uma propriedade Item para fornecer suporte à indexação de matrizes e implementa três versões do GetSlice. Se você puder usar esse código como um modelo para seus tipos de matriz, poderá usar todas as operações de fatiamento descritas nesta seção.

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

Funções booleanas em matrizes

As funções Array.exists e Array.exists2 os elementos de teste em uma ou duas matrizes, respectivamente. Essas funções assumem uma função de teste e retornam true se houver um elemento (ou par de elementos para Array.exists2) que satisfaça a condição.

O código a seguir demonstra o uso de Array.exists e Array.exists2. Nesses exemplos, novas funções são criadas aplicando apenas um dos argumentos, nesses casos, o argumento function.

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

A saída do código anterior é a seguinte.

true
false
false
true

Da mesma forma, a função Array.forall testa uma matriz para determinar se cada elemento satisfaz uma condição booleana. A variação Array.forall2 faz a mesma coisa usando uma função booleana que envolve elementos de duas matrizes de comprimento igual. O código a seguir ilustra o uso dessas funções.

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

A saída para estes exemplos é a seguinte.

false
true
true
false

Pesquisar matrizes

Array.find toma uma função booleana e retorna o primeiro elemento para o qual a função retorna true, ou gera um System.Collections.Generic.KeyNotFoundException se nenhum elemento que satisfaz a condição for encontrado. Array.findIndex é como Array.find, exceto que ele retorna o índice do elemento em vez do elemento em si.

O código a seguir usa Array.find e Array.findIndex para localizar um número que é um quadrado perfeito e um cubo perfeito.

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

A saída é a seguinte.

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

Array.tryFind é como Array.find, exceto que seu resultado é um tipo de opção e retorna None se nenhum elemento for encontrado. Array.tryFind deve ser usado em vez de Array.find quando você não sabe se um elemento correspondente está na matriz. Da mesma forma, Array.tryFindIndex é como Array.findIndex exceto que o tipo de opção é o valor de retorno. Se nenhum elemento for encontrado, a opção será None.

O código a seguir demonstra o uso do Array.tryFind. Este código depende do código anterior.

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

A saída é a seguinte.

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

Use Array.tryPick quando precisar transformar um elemento, além de encontrá-lo. O resultado é o primeiro elemento para o qual a função retorna o elemento transformado como um valor de opção, ou None se nenhum elemento for encontrado.

O código a seguir mostra o uso de Array.tryPick. Nesse caso, em vez de uma expressão lambda, várias funções auxiliares locais são definidas para simplificar o código.

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

A saída é a seguinte.

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.

Executar cálculos em arrays

A Array.average função retorna a média de cada elemento em uma matriz. É limitado a tipos de elementos que suportam a divisão exata por um inteiro, que inclui tipos de ponto flutuante, mas não tipos integrais. A Array.averageBy função retorna a média dos resultados da chamada de uma função em cada elemento. Para uma matriz de tipo integral, você pode usar Array.averageBy e fazer com que a função converta cada elemento em um tipo de ponto flutuante para o cálculo.

Use Array.max ou Array.min para obter o elemento máximo ou mínimo, se o tipo de elemento oferecer suporte a ele. Da mesma forma, Array.maxBy e Array.minBy permitir que uma função seja executada primeiro, talvez para se transformar em um tipo que suporte a comparação.

Array.sum Adiciona os elementos de uma matriz, Array.sumBy chama uma função em cada elemento e adiciona os resultados juntos.

Para executar uma função em cada elemento de uma matriz sem armazenar os valores de retorno, use Array.iter. Para uma função envolvendo duas matrizes de comprimento igual, use Array.iter2. Se você também precisar manter uma matriz dos resultados da função, use Array.map ou Array.map2, que opera em duas matrizes de cada vez.

As variações Array.iteri e Array.iteri2 permitem que o índice do elemento seja envolvido no cálculo, o mesmo é verdade para Array.mapi e Array.mapi2.

As funções Array.fold, Array.foldBack, Array.reduce, Array.reduceBack, Array.scan, e Array.scanBack executam algoritmos que envolvem todos os elementos de uma matriz. Da mesma forma, as variações Array.fold2 e Array.foldBack2 realizar cálculos em duas matrizes.

Estas funções para a realização de cálculos correspondem às funções com o mesmo nome no módulo List. Para exemplos de uso, consulte Listas.

Modificar matrizes

Array.set Define um elemento como um valor especificado. Array.fill Define um intervalo de elementos em uma matriz para um valor especificado. O código a seguir fornece um exemplo de Array.fill.

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

A saída é a seguinte.

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

Você pode usar Array.blit para copiar uma subseção de uma matriz para outra matriz.

Converter de e para outros tipos

Array.ofList Cria uma matriz a partir de uma lista. Array.ofSeq cria uma matriz a partir de uma sequência. Array.toList e converta Array.toSeq para esses outros tipos de coleção a partir do tipo de matriz.

Classificar matrizes

Use Array.sort para classificar uma matriz usando a função de comparação genérica. Use Array.sortBy para especificar uma função que gera um valor, referido como uma chave, para classificar usando a função de comparação genérica na chave. Use Array.sortWith se quiser fornecer uma função de comparação personalizada. Array.sort, Array.sortBye Array.sortWith todos retornam a matriz classificada como uma nova matriz. As variações Array.sortInPlace, Array.sortInPlaceBye Array.sortInPlaceWith modificam a matriz existente em vez de retornar uma nova.

Matrizes e tuplas

As funções Array.zip e Array.unzip converter matrizes de pares de tuplas em tuplas de matrizes e vice-versa. Array.zip3 e Array.unzip3 são semelhantes, exceto que trabalham com tuplas de três elementos ou tuplas de três matrizes.

Cálculos paralelos em matrizes

O módulo Array.Parallel contém funções para realizar cálculos paralelos em matrizes. Este módulo não está disponível em aplicativos que visam versões do .NET Framework anteriores à versão 4.

Consulte também