Compartilhar via


Matrizes (F#)

Matrizes são coleções de tamanho fixo, de base zero, mutáveis de elementos de dados consecutivos que são todos do mesmo tipo.

Criando matrizes

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

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

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

let array1 = 
    [|
        1
        2
        3
     |]

O tipo dos elementos da matriz é inferido dos literais usados e deve ser consistente. O código a seguir causa um erro porque 1,0 é um float e 2 e 3 são números inteiros.

// Causes an error.
// let array2 = [| 1.0; 2; 3 |] 

Você também pode usar expressões de sequência para criar matrizes. A seguir, 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 em que todos os elementos são inicializados a zero, use Array.zeroCreate.

let arrayOfTenZeroes : int array = Array.zeroCreate 10

Acessando Elementos

Você pode acessar os elementos da matriz usando um operador ponto (.) e colchetes ([ e ]).

array1.[0]

Os índices da matriz começam em 0.

Você também pode acessar os elementos da matriz usando a notação de fatia, que permite que você especifique uma subclassificação da matriz. A seguir, 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 matriz e módulos

O tipo de todas as matrizes F# é o tipo Arraydo .NET Framework. Portanto, as matrizes F# oferecem suporte a todas as funcionalidades disponíveis em Array.

O módulo de biblioteca Microsoft.FSharp.Collections.Array tem suporte para operações em matrizes unidimensionais. Os módulos Array2D, Array3D e Array4D contêm funções que aceitam operações em matrizes de duas, três e quatro dimensões, respectivamente. Você pode criar matrizes de classificação maior que quatro usando Array.

Funções simples

Array.get obtém um elemento. Array.length fornece o comprimento de uma matriz. Array.set define um elemento para um valor especificado. O exemplo de código a seguir ilustra o uso destas 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 cria matrizes

Várias funções criam matrizes sem exigir 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 para os 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 de 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 os 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 somente 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 está presente em secondArray. A cadeia de caracteres Test2 aparece em ambas as matrizes como a operação de Insert no tipo de StringBuilder afeta o objeto subjacente de StringBuilder , que é referenciado em ambas as matrizes.

Array.sub gerencia uma nova matriz de um subintervalo de uma matriz. Você especifica a subclassificação 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 os elementos de uma matriz para incluir em uma nova matriz. O código a seguir demonstra Array.choose. 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 - 1, como 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 da matriz de uma matriz existente e, em seguida, obtém os elementos gerados pela função e os combina em uma nova matriz. O código a seguir demonstra Array.collect.

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 usa uma sequência de matrizes e as combina em uma única matriz. O código a seguir demonstra Array.concat.

let multiplicationTable max = seq { for i in 1 .. max -> [| for j in 1 .. max -> (i, j, i*j) |] }
printfn "%A" (Array.concat (multiplicationTable 3))

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

[|(1, 1, 1); (1, 2, 2); (1, 3, 3); (2, 1, 2); (2, 2, 4); (2, 3, 6); (3, 1, 3);
  (3, 2, 6); (3, 3, 9)|]

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

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 gerencia uma nova matriz invertendo a ordem de uma matriz existente. O código a seguir demonstra Array.rev.

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 as funções no módulo de matriz que tornam 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á nenhuma sintaxe para gravar um literal de matriz multidimensional. Use o operador array2D para criar uma matriz de uma sequência de sequências de elementos da matriz. As sequências podem ser literais de matriz ou de 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 têm uma função que é usada para criar os elementos. Para criar uma matriz bidimensional que contém os elementos definidos para um valor inicial em vez de especificar uma função, use a função Array2D.create, que também está disponível para matrizes de até quatro dimensões. O exemplo de código a seguir primeiro mostra 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 indexação da matriz e a sintaxe de recorte são suportadas para matrizes até a posição 4. Ao especificar um índice em várias dimensões, use vírgulas para separar os índices conforme ilustrado no exemplo de código.

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 das dimensões acima.

Apenas um subconjunto de funções disponíveis para matrizes unidimensionais também fica disponível para matrizes multidimensionais. Para obter mais informações, consulte Módulo Collections.Array (F#), Módulo Collections.Array2D (F#), Módulo Collections.Array3D (F#) e Módulo Collections.Array4D (F#).

Recorte de matriz e matrizes multidimensionais

Em uma matriz bidimensional (uma matriz), você pode extrair uma submatriz ao especificar intervalos e usar um caractere curinga (*) para especificar colunas ou linhas 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]

A partir do F# 3.1, você pode decompor uma matriz multidimensional em submatrizes de mesma dimensão ou menor. 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 corte para tipos que implementam os operadores de acesso do elemento e métodos sobrecarregados de GetSlice . Por exemplo, o código a seguir cria um tipo de matriz que encapsula a matriz 2D do F# em um wrapper, implementa uma propriedade Item para fornecer suporte para indexação de matriz e implementa três versões de GetSlice. Se você puder usar esse código como um modelo para seus tipos de matriz, poderá usar todas as operações de recorte que esta seção descreve.

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 "%A" submatrix

    let firstRow = test1.[0,*]
    let secondRow = test1.[1,*]
    let firstCol = test1.[*,0]
    printfn "%A" firstCol

Funções booleanas em matrizes

As funções Array.exists e Array.exists2 testam elementos em uma ou duas matrizes, respectivamente. Essas funções têm uma função de teste e um retorno true se há um elemento (ou par de elementos para Array.exists2) que satisfaz a condição.

O código a seguir demonstra o uso do Array.exists e Array.exists2. Nestes exemplos, novas funções são criadas por meio da aplicação de apenas um dos argumentos, nestes casos, o argumento de função.

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

De maneira semelhante, a função Array.forall testa uma matriz para determinar se cada elemento satisfaz uma condição booliana. A variação Array.forall2 faz a mesma coisa usando uma função booleana que quebra os elementos de duas matrizes de comprimento igual. O código a seguir ilustra o uso destas 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 destes exemplos são:

false
true
true
false

Procurando matrizes

Array.find usa uma função booleana e retorna o primeiro elemento para o qual a função retorna true, ou aumenta KeyNotFoundException se nenhum elemento que satisfaz a condição for encontrado. Array.findIndex é como Array.find, exceto que retorna o índice de um elemento em vez do próprio elemento.

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 o 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 souber se um elemento correspondente está na matriz. De maneira semelhante, Array.tryFindIndex é como Array.findIndex, exceto pelo tipo de opção, que é 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. Esse 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

Use Array.tryPick quando você precisar transformar um elemento além do localizá-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 esse 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.

Realizando cálculos em matrizes

A função Array.average retorna a média de cada elemento em uma matriz. É limitada aos tipos de elemento que oferecem suporte à divisão exata por um número inteiro, o que inclui tipos de ponto flutuante mas não tipos integrais. A função Array.averageBy retorna a média dos resultados de chamada de uma função em cada elemento. Para uma matriz de tipo integral, você pode usar Array.averageBy e fazer a função converter cada elemento para um tipo de ponto flutuante para a computação.

Use Array.max ou Array.min para obter o elemento mínimo ou máximo, se o tipo do elemento suportá-lo. De maneira semelhante, Array.maxBy e Array.minBy permitem que uma função seja executada primeiro, talvez para se transformar em um tipo que ofereça suporte à comparação.

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

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

As variações Array.iteri e Array.iteri2 permitem que o índice de um elemento seja envolvido no cálculo; o mesmo vale para Array.mapi e Array.mapi2.

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

Essas funções para executar cálculos correspondem às funções de mesmo nome no Módulo de lista. Para exemplos de uso, consulte Listas (F#).

Modificando matrizes

Array.set define um elemento para 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 seção de uma matriz a outra matriz.

Convertendo 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 Array.toSeq convertem para esses outros tipos de coleção do tipo de matriz.

Matrizes de classificação

Use Array.sort para classificar uma matriz usando a função genérica de comparação. Use Array.sortBy para especificar uma função que gerencia um valor, conhecida como uma chave para classificar usando a função genérica de comparação na chave. Use Array.sortWith se você desejar fornecer uma função personalizada de comparação. Array.sort, Array.sortBy, e Array.sortWith retornar a matriz classificada como uma nova matriz. As variações Array.sortInPlace, Array.sortInPlaceBy e Array.sortInPlaceWith modificam a matriz existente, em vez de retornar uma nova.

Matrizes e tuples

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

Cálculos paralelos em matrizes

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

Consulte também

Outros recursos

Referência da linguagem F#

Tipos F#