Um dos principais objetivos do Julia é conseguir fazer cálculos sobre dados numéricos, de forma rápida e eficiente. Muitos dados numéricos.
Normalmente, os dados ficam guardados em arrays (ou nalgum tipo relacionado), por isso é razoável dizer que os arrays estão no coração da linguagem Julia.
Os arrays podem ter um tamanho arbitrário (limitado apenas pela memória do teu hardware) e podem ter um número arbitrário de dimensões.
Alguns tipos de arrays têm nomes especiais (copiados da Álgebra Linear).
Um array unidimensional chama-se Vector e um array bidimensional chama-se Matrix.
Ambos são subtipos de Array em Julia.
Este conceito introdutório centra-se nos fundamentos dos vetores. Os arrays com mais dimensões serão abordados mais à frente no programa.
Em Julia, um vetor é uma sequência ordenada de valores aos quais podes aceder por um número de índice.
A forma mais simples de criar um vetor é listar os valores entre parênteses retos:
julia> num_vec = [1, 4, 9]
3-element Vector{Int64}:
1
4
9
julia> str_vec = ["arrays", "are", "important"]
3-element Vector{String}:
"arrays"
"are"
"important"
O tipo do vetor corresponde ao tipo de cada elemento.
Tecnicamente, é um vetor coluna, mas ignora isso por agora se não estiveres familiarizado com Álgebra Linear. Isto fará mais sentido depois do conceito de arrays multidimensionais.
Então os vetores têm de ser homogéneos (todos os elementos do mesmo tipo)?
De certa forma, sim, e recomenda-se que procures isso se quiseres o melhor desempenho.
No entanto, o Julia consegue contornar esta limitação (quando necessário) usando o tipo Any:
julia> mixed_vector = [1, "str", 'c']
3-element Vector{Any}:
1
"str"
'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
Quando crias um vetor vazio, o Julia assume o tipo de elemento Any, mas isso pode ser definido de forma mais específica anotando o array vazio com o tipo de elemento pretendido.
julia> empty_vec = []
Any[]
julia> empty_int_vec = Int64[]
Int64[]
É muito comum começar com vetores de valores todos 0 ou todos 1. Para isso existem funções chamadas (como seria de esperar) zeros() e ones(), que recebem o tamanho do vetor como parâmetro.
O tipo predefinido é Float64, mas podes indicar opcionalmente outros tipos numéricos.
De forma semelhante, a função fill() recebe tanto um valor a repetir como o tamanho pretendido do vetor.
julia> zeros(3)
3-element Vector{Float64}:
0.0
0.0
0.0
julia> ones(3)
3-element Vector{Float64}:
1.0
1.0
1.0
julia> ones(Int64, 3)
3-element Vector{Int64}:
1
1
1
julia> fill(42, 3)
3-element Vector{Int64}:
42
42
42
Há muitas outras opções: consulta o manual.
Lê e memoriza esta regra:
Isto é familiar para quem já usou outras linguagens de ciência de dados (R, Matlab, Fortran...), mas pode parecer chocante para informáticos com experiência em linguagens do tipo C.
Fora isso, os índices funcionam como provavelmente imaginas: basta colocá-los entre parênteses retos:
squares = [0, 1, 4, 9, 16]
squares[1] # 0
squares[3] # 4
squares[begin] # 0 ("begin" is synonym for 1)
squares[end] # 16 ("end" is synonym for length(squares))
Repara na comodidade de indexar com end (que é muito útil) e begin (que provavelmente não é).
Os programadores de Python podem estar a perguntar-se sobre índices negativos.
Não vale a pena: não fazem parte do Julia e lançam um BoundsError, tal como qualquer índice inferior a 1 ou superior a length(squares).
Os vetores em Julia são mutáveis: podemos alterar o conteúdo de posições individuais.
julia> vals = [1, 3, 5, 7]
4-element Vector{Int64}:
1
3
5
7
julia> vals[2] = 4 # reassign the value of this element
4
# Only the value at position 2 changes:
julia> vals
4-element Vector{Int64}:
1
4
5
7
Há muitas funções disponíveis para manipular vetores, embora a documentação do Julia as generalize para "coleções" em vez de vetores.
Repara que, por convenção, as funções que modificam os seus argumentos têm um ! no nome.
O compilador não te obriga a isso, mas é uma convenção muito forte no mundo do Julia.
Estas são algumas das funções úteis:
push!().pop!().pushfirst!() e popfirst!().insert!() e deleteat!().julia> vals = [1, 3]
2-element Vector{Int64}:
1
3
julia> push!(vals, 5, 6) # can add multiple values
4-element Vector{Int64}:
1
3
5
6
julia> pop!(vals) # mutates vals, return popped value
6
julia> vals
3-element Vector{Int64}:
1
3
5
A concatenação é bastante simples para vetores, embora uma vista de olhos pelo manual deixe antever as complexidades que nos esperam quando considerarmos arrays multidimensionais.
A função append!() pode receber como valores de entrada uma mistura de vários vetores e de elementos isolados.
julia> append!([1, 2], [3, 4], [-1, -2], 15)
7-element Vector{Int64}:
1
2
3
4
-1
-2
15