Exercício – Usar matrizes

Concluído

As matrizes do Go são uma estrutura de dados de comprimento fixo de um tipo específico. Elas podem ter zero ou mais elementos, e você precisa definir o tamanho ao declará-las ou inicializá-las. Além disso, não é possível redimensioná-las depois que elas são criadas. Por esses motivos, as matrizes não costumam ser usadas em programas Go, mas são a base de fatias e mapas.

Declarar matrizes

Para declarar uma matriz no Go, você precisa definir o tipo de dados dos elementos e o número de elementos que a matriz pode conter. Em seguida, você pode acessar cada elemento na matriz com a notação de subscrito, em que zero é o primeiro elemento e o último elemento é um menor que o comprimento da matriz (comprimento -1).

Por exemplo, vamos usar o seguinte código:

package main

import "fmt"

func main() {
    var a [3]int
    a[1] = 10
    fmt.Println(a[0])
    fmt.Println(a[1])
    fmt.Println(a[len(a)-1])
}

Ao executar o código anterior, você obterá a seguinte saída:

0
10
0

Embora você tenha declarado uma matriz, não recebe um erro ao acessar os elementos dela. Por padrão, o Go inicializa cada elemento com o tipo de dados padrão. Nesse caso, o valor padrão de int é zero. Mas você pode atribuir um valor a uma posição específica, como fizemos com a[1] = 10. Além disso, você pode acessar esse elemento usando a mesma notação. Observe também que, para referenciar o primeiro elemento, usamos a[0]. Para referenciar o último elemento, usamos a[len(a)-1]. A função len é uma função interna do Go usada para obter o número de elementos em uma matriz, uma fatia ou um mapa.

Inicializar matrizes

Você também poderá inicializar uma matriz com outros valores além dos padrões quando estiver declarando uma matriz. Por exemplo, você pode usar o seguinte código para ver e testar a sintaxe:

package main

import "fmt"

func main() {
    cities := [5]string{"New York", "Paris", "Berlin", "Madrid"}
    fmt.Println("Cities:", cities)
}

Execute o código anterior e você verá esta saída:

Cities: [New York Paris Berlin Madrid ]

Embora a matriz precise ter cinco elementos, não precisamos atribuir um valor a todos os elementos. Como já vimos, a última posição tem uma cadeia de caracteres vazia, porque é o valor padrão de um tipo de dados String.

Reticências em matrizes

Outra maneira de declarar e inicializar uma matriz quando você não sabe de quantas posições precisará, mas conhece o conjunto de elementos de dados, é usar reticências (...), como neste exemplo:

q := [...]int{1, 2, 3}

Vamos modificar o programa que usamos na seção anterior para usar reticências. O código ficará parecido com este exemplo:

package main

import "fmt"

func main() {
    cities := [...]string{"New York", "Paris", "Berlin", "Madrid"}
    fmt.Println("Cities:", cities)
}

Execute o código anterior e você verá uma saída semelhante, como neste exemplo:

Cities: [New York Paris Berlin Madrid]

Consegue ver a diferença? Não há nenhuma cadeia de caracteres vazia no final. O comprimento da matriz foi determinado pelas cadeias de caracteres que você colocou ao inicializá-la. Você não está reservando memória que não sabe se vai acabar precisando.

Outra maneira interessante de inicializar uma matriz é usar reticências e só especificar um valor para a última posição. Por exemplo, use o seguinte código:

package main

import "fmt"

func main() {
    numbers := [...]int{99: -1}
    fmt.Println("First Position:", numbers[0])
    fmt.Println("Last Position:", numbers[99])
    fmt.Println("Length:", len(numbers))
}

Execute este código e você obterá esta saída:

First Position: 0
Last Position: -1
Length: 100

Observe como o comprimento da matriz é 100 porque você especificou um valor para a posição 99º. A primeira posição imprime o valor padrão (zero).

Matrizes multidimensionais

O Go apresenta suporte para matrizes multidimensionais quando você precisa trabalhar com estruturas de dados complexas. Vamos criar um programa no qual você declara e inicializa uma matriz bidimensional. Use o seguinte código:

package main

import "fmt"

func main() {
    var twoD [3][5]int
    for i := 0; i < 3; i++ {
        for j := 0; j < 5; j++ {
            twoD[i][j] = (i + 1) * (j + 1)
        }
        fmt.Println("Row", i, twoD[i])
    }
    fmt.Println("\nAll at once:", twoD)
}

Execute o programa anterior e você verá uma saída como este exemplo:

Row 0 [1 2 3 4 5]
Row 1 [2 4 6 8 10]
Row 2 [3 6 9 12 15]

All at once: [[1 2 3 4 5] [2 4 6 8 10] [3 6 9 12 15]]

Você declarou uma matriz bidimensional que especifica quantas posições a matriz terá na segunda dimensão, desta forma: var twoD [3][5]int. Considere essa matriz como uma estrutura de dados com colunas e linhas, como uma planilha ou uma matriz. Neste ponto, todas as posições têm um valor padrão igual a zero. No loop for, estamos inicializando cada posição com um padrão de valor diferente em cada linha. Por fim, você imprimirá todos os valores no terminal.

E se você quiser declarar uma matriz tridimensional? Bem, você consegue imaginar qual é a sintaxe, certo? Faça isso como neste exemplo:

package main

import "fmt"

func main() {
    var threeD [3][5][2]int
    for i := 0; i < 3; i++ {
        for j := 0; j < 5; j++ {
            for k := 0; k < 2; k++ {
                threeD[i][j][k] = (i + 1) * (j + 1) * (k + 1)
            }
        }
    }
    fmt.Println("\nAll at once:", threeD)
}

Execute o código anterior e você verá uma saída como este exemplo:

All at once: [[[1 2] [2 4] [3 6] [4 8] [5 10]] [[2 4] [4 8] [6 12] [8 16] [10 20]] [[3 6] [6 12] [9 18] [12 24] [15 30]]]

Se formatarmos a saída em um formato mais legível, você poderá ter algo semelhante a este exemplo:

All at once: 
[
    [
        [1 2] [2 4] [3 6] [4 8] [5 10]
    ] 
    [
        [2 4] [4 8] [6 12] [8 16] [10 20]
    ] 
    [
        [3 6] [6 12] [9 18] [12 24] [15 30]
    ]
]

Observe como a estrutura é alterada de uma matriz bidimensional. Você pode continuar tendo mais dimensões conforme necessário, mas vamos parar aqui por enquanto, pois temos outros tipos de dados para explorar.