Nel concetto Vectors, abbiamo detto che «gli array sono al cuore del linguaggio Julia» e che un vettore è unidimensionale.
Detto questo, potremmo ragionevolmente sperare che il linguaggio offra molti modi versatili e potenti per fare cose con i vettori, qualunque cosa significhi.
Alcune funzioni molto semplici ricevono un vettore in input e restituiscono un output scalare.
v = [2, 3, 4]
length(v) # => 3
sum(v) # => 9
Supponi di avere un vettore numerico e di voler sottrarre 0.5 da ogni valore.
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)
Il tentativo fallisce, quindi proviamo a sottrarre un altro vettore?
julia> v - [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
Funziona, ma diventa piuttosto noioso e avido di memoria man mano che i vettori si allungano.
Per fortuna Julia ha un punto «magico» che risolve questo problema in modo semplicissimo: v .- 0.5 è tutto ciò che ti serve.
La sezione successiva spiega perché.
Dunque, v - 0.5 fallisce ma v .- 0.5 funziona: dobbiamo capire cosa fa quel punto.
Sono due cose, che combinate danno il risultato desiderato.
Innanzitutto, aggiungere un punto prima di un qualsiasi operatore infisso significa «applica questa operazione a ogni elemento separatamente».
Allo stesso modo, aggiungere un punto dopo il nome di una funzione la «vettorizza», anche se la funzione è stata scritta per input scalari.
julia> sqrt.([1, 4, 9])
3-element Vector{Float64}:
1.0
2.0
3.0
In un esempio precedente abbiamo visto che possiamo sottrarre vettori di uguale lunghezza. Tieni però presente che .- è un operatore più sicuro di -, perché rende esplicita l'intenzione di agire elemento per elemento.
julia> v .- [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
E se i vettori hanno lunghezze diverse?
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
In generale, lunghezze diverse sono un errore, tranne quando una delle due vale 1 (tecnicamente, una dimensione «singleton»).
I singleton come [0.5,] o semplicemente 0.5 vengono espansi automaticamente alla lunghezza necessaria, ripetendoli.
Questo è il cuore del broadcasting.
La selezione degli elementi di un vettore tramite il numero di indice è stata trattata nei concetti precedenti.
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']
È anche possibile selezionare gli elementi che soddisfano una certa espressione logica (tecnicamente, un «predicato»). Di solito questo richiede il 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)
Nelle espressioni più complesse i punti tendono a moltiplicarsi (ma sono piccoli e facili da digitare).
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)
Ricorda che il «vettore» può in realtà essere qualsiasi iterabile ordinato adatto allo scopo, come un intervallo:
julia> n = 3:10
3:10
julia> n[isodd.(n)]
4-element Vector{Int64}:
3
5
7
9
Sei un appassionato osservatore di uccelli che tiene traccia di quanti uccelli hanno visitato il tuo giardino negli ultimi sette giorni.
Hai sei compiti, che riguardano tutti il numero di uccelli che hanno visitato il tuo giardino.
Implementa la funzione today() in modo che restituisca quanti uccelli hanno visitato il tuo giardino oggi.
I conteggi degli uccelli sono ordinati per giorno: il primo elemento è il conteggio del giorno più lontano, l'ultimo è il conteggio di oggi.
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> today(birds_per_day)
1
Implementa la funzione increment_todays_count() per incrementare il conteggio di oggi:
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> increment_todays_count(birds_per_day)
[2, 5, 0, 7, 4, 2]
Implementa la funzione has_day_without_birds() che restituisce true se c'è stato un giorno in cui zero uccelli hanno visitato il giardino; altrimenti, restituisce false:
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> has_day_without_birds(birds_per_day)
true
Implementa la funzione count_for_first_days() che restituisce il numero di uccelli che hanno visitato il tuo giardino dall'inizio della settimana, ma limita il conteggio al numero di giorni specificato dall'inizio della settimana.
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> count_for_first_days(birds_per_day, 4)
14
Alcuni giorni sono più intensi di altri.
Un giorno intenso è quello in cui cinque o più uccelli hanno visitato il tuo giardino.
Implementa la funzione busy_days() in modo che restituisca il numero di giorni intensi:
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> busy_days(birds_per_day)
2
Hai deciso di ampliare i tuoi registri tenendo i conteggi per più settimane. In ogni caso, i conteggi sono disposti per giorno della settimana, dal lunedì come primo elemento alla domenica come ultimo.
Implementa la funzione average_per_day() che restituisce la media per 2 settimane.
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]
Iscriviti a Exercism per imparare e padroneggiare Julia con 35 concetti128 esercizi e il mentoring di persone reali, tutto gratis.