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
, Array3D
e 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.choose
o . 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 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.collect
o .
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.concat
o .
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.filter
o .
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.rev
o .
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.sortBy
e Array.sortWith
todos retornam 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 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.