No Conceito Vectors, dissemos que «os arrays estão no coração da linguagem Julia» e que um vetor é unidimensional.
Assim sendo, é razoável esperar que a linguagem ofereça muitas formas versáteis e poderosas de fazer coisas com vetores, seja lá o que isso for.
Algumas funções muito simples recebem um vetor como parâmetro de entrada e devolvem um valor escalar.
v = [2, 3, 4]
length(v) # => 3
sum(v) # => 9
Suponhamos que tens um vetor numérico e queres subtrair 0.5 a cada valor.
julia> v = [1.2, 1.5, 1.7]
3-element Vector{Float64}:
1.2
1.5
1.7
julia> v - 0.5
ERROR: MethodError: no method matching -(::Vector{Float64}, ::Float64)
Isso falha, então e se subtraíres outro vetor?
julia> v - [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
Funciona, mas torna-se bastante maçador e exige muita memória à medida que os vetores ficam maiores.
Felizmente, a Julia tem um ponto «mágico» que resolve este problema de forma muito simples: só precisas de v .- 0.5.
A secção seguinte explica porquê.
Então, v - 0.5 falha, mas v .- 0.5 funciona, e precisamos de perceber o que faz o ponto.
Duas coisas, que se combinam para dar o resultado desejado.
Primeiro, colocar um ponto antes de qualquer operador infixo significa «aplicar esta operação a cada elemento separadamente».
Da mesma forma, colocar um ponto depois do nome de uma função «vetoriza-a», mesmo que a função tenha sido escrita para parâmetros de entrada escalares.
julia> sqrt.([1, 4, 9])
3-element Vector{Float64}:
1.0
2.0
3.0
Vimos num exemplo anterior que podemos subtrair vetores com o mesmo comprimento, mas convém perceberes que .- é um operador mais seguro do que -, porque deixa clara a intenção de operar elemento a elemento.
julia> v .- [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
E quanto a vetores com comprimentos diferentes?
julia> v .- [0.5, 0.5]
ERROR: DimensionMismatch: arrays could not be broadcast to a common size
julia> v .- [0.5,]
3-element Vector{Float64}:
0.7
1.0
1.2
Em geral, comprimentos diferentes são um erro, exceto quando um deles tem comprimento 1 (tecnicamente, uma dimensão «singleton»).
Os singletons como [0.5,] ou apenas 0.5 são expandidos automaticamente até ao comprimento necessário, por repetição.
É isto que está no coração do broadcasting.
A seleção de elementos de um vetor por número de índice já foi abordada em Conceitos anteriores.
a = collect('A':'Z') # => 26-element Vector{Char}
# index with an integer
a[2] # => 'B'
# index with a range
a[12:2:18] # => ['L', 'N', 'P, 'R']
# index with another vector
a[ [1, 3, 5] ] # => ['A', 'C', 'E']
Também é possível selecionar elementos que satisfazem determinada expressão lógica (tecnicamente, um «predicado»). Normalmente, isso exige broadcasting.
julia> a[a .< 'D']
3-element Vector{Char}:
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)
'C': ASCII/Unicode U+0043 (category Lu: Letter, uppercase)
Em expressões mais complexas, os pontos tendem a multiplicar-se (mas são pequenos e fáceis de escrever).
julia> a[a .< 'D' .|| a .> 'W']
6-element Vector{Char}:
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)
'C': ASCII/Unicode U+0043 (category Lu: Letter, uppercase)
'X': ASCII/Unicode U+0058 (category Lu: Letter, uppercase)
'Y': ASCII/Unicode U+0059 (category Lu: Letter, uppercase)
'Z': ASCII/Unicode U+005A (category Lu: Letter, uppercase)
Lembra-te de que o «vetor» pode, na verdade, ser qualquer iterável ordenado adequado, como um intervalo:
julia> n = 3:10
3:10
julia> n[isodd.(n)]
4-element Vector{Int64}:
3
5
7
9
És um observador de aves ávido que regista quantas aves visitaram o teu jardim nos últimos sete dias.
Tens seis tarefas, todas relacionadas com o número de aves que visitaram o teu jardim.
Implementa a função today() para devolver quantas aves visitaram o teu jardim hoje.
As contagens de aves estão ordenadas por dia: o primeiro elemento é a contagem do dia mais antigo e o último é a contagem de hoje.
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> today(birds_per_day)
1
Implementa a função increment_todays_count() para incrementar a contagem de hoje:
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> increment_todays_count(birds_per_day)
[2, 5, 0, 7, 4, 2]
Implementa a função has_day_without_birds() que devolve true se houve um dia em que zero aves visitaram o jardim; caso contrário, devolve false:
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> has_day_without_birds(birds_per_day)
true
Implementa a função count_for_first_days() que devolve o número de aves que visitaram o teu jardim desde o início da semana, mas limita a contagem ao número de dias indicado a partir do início da semana.
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> count_for_first_days(birds_per_day, 4)
14
Há dias mais movimentados do que outros.
Um dia movimentado é aquele em que cinco ou mais aves visitaram o teu jardim.
Implementa a função busy_days() para devolver o número de dias movimentados:
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> busy_days(birds_per_day)
2
Decides ampliar os teus registos, guardando contagens de várias semanas. Em cada caso, as contagens estão organizadas por dia da semana, de segunda-feira como primeira entrada a domingo como última.
Implementa a função average_per_day() que devolve a média de 2 semanas.
julia> week1 = [7, 2, 9, 1, 3, 0, 10]
julia> week2 = [2, 6, 4, 1, 3, 8, 9]
julia> average_per_day(week1, week2)
[4.5, 4.0, 6.5, 1.0, 4.0, 3.0, 9.5]
Inscreve-te no Exercism para aprenderes e dominares Julia com 35 conceitos128 exercícios, e mentoria humana real, tudo grátis.