Compartilhar via


Matrizes (F#)

As matrizes são coleções mutáveis de tamanho fixo, baseado em zero e mutáveis 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 matriz pequena listando valores consecutivos entre [| 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, 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.

// 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. Veja 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 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 ponto original (.[index]) ainda tem suporte, 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 a notação de fatia, que permite especificar um subconjunto da matriz. Exemplos de notação de fatia a seguir.

// 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 e módulos de matriz

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

O Array módulo dá suporte a operações em matrizes unidimensionais. Os módulos Array2D, Array3De Array4D contêm funções que dão suporte a 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 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 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 para 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 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, firstArray mas não afeta a referência original a uma cadeia de caracteres vazia que ainda está presente em 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 de um subconjunto de uma matriz. Especifique o subconjunto fornecendo o índice inicial e o comprimento. O código a seguir demonstra o uso de Array.sub.

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

A saída mostra que a subarray 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 a serem incluídos 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 - 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.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.

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 booliana e gera uma nova matriz que contém apenas esses 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 gera uma nova matriz revertendo 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 combinar facilmente 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 um literal de matriz multidimensional. 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 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 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 para gerar a matriz bidimensional Array2D.init 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 de fatiamento de matriz tem suporte para matrizes até a classificação 4. Quando você especifica um índice em várias dimensões, 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.

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

Fatiamento de matriz e matrizes multidimensionais

Em uma matriz bidimensional (uma matriz), você pode extrair uma sub matriz 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 subarrays da mesma dimensão ou inferior. 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 de elementos e métodos sobrecarregados GetSlice . Por exemplo, o código a seguir cria um tipo matrix que encapsula a matriz F# 2D, 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 os tipos de matriz, poderá usar todas as operações de fatiamento 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 $"{submatrix}"

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

Funções boolianas em matrizes

As funções e Array.exists2 os elementos Array.exists 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 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

Da mesma forma, 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 booliana 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 esses exemplos é a seguinte.

false
true
true
false

Matrizes de pesquisa

Array.find usa uma função booliana e retorna o primeiro elemento para o qual a função retorna trueou gera um System.Collections.Generic.KeyNotFoundException elemento se nenhum elemento que satisfaça a condição for encontrado. Array.findIndex é como Array.find, exceto que ele retorna o índice do elemento em vez do próprio elemento.

O código a seguir usa Array.find e Array.findIndex para localizar um número que seja um cubo quadrado perfeito e 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 retornado. Se nenhum elemento for encontrado, a opção será None.

O código a seguir demonstra o uso de 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
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 desse tipo é 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 matrizes

A Array.average função retorna a média de cada elemento em uma matriz. Ele é limitado a tipos de elementos que dão suporte à 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 a computação.

Use Array.max ou Array.min obtenha o elemento máximo ou mínimo, se o tipo de elemento der suporte a ele. Da mesma forma, Array.maxBy e Array.minBy permitir que uma função seja executada primeiro, talvez para transformar em um tipo que dê 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 retornados, use Array.iter. Para uma função que envolva 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 esteja envolvido na computação; o mesmo é verdadeiro para Array.mapi e Array.mapi2.

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

Essas funções para executar cálculos correspondem às funções de mesmo nome no módulo Lista. Para obter 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 Array.toSeq converta para esses outros tipos de coleção 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, conhecido como chave, para classificar usando a função de comparação genérica na chave. Use Array.sortWith se você 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 modificar a matriz existente em vez de retornar uma nova.

Matrizes e tuplas

As funções Array.zip e Array.unzip a conversão de matrizes de pares de tupla em tuplas de matrizes e vice-versa. Array.zip3 e Array.unzip3 são semelhantes, exceto que funcionam 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 executar cálculos paralelos em matrizes. Este módulo não está disponível em aplicativos destinados a versões do .NET Framework antes da versão 4.

Consulte também