Dans le concept Vectors, on a dit que « les tableaux sont au cœur du langage Julia » et qu'un vecteur est un tableau à une dimension.
Partant de là, on peut raisonnablement espérer que le langage offre de nombreuses façons polyvalentes et puissantes de faire des choses avec les vecteurs, quoi que cela signifie.
Certaines fonctions très simples prennent un vecteur en entrée et renvoient une valeur scalaire.
v = [2, 3, 4]
length(v) # => 3
sum(v) # => 9
Suppose que tu aies un vecteur numérique et que tu veuilles soustraire 0,5 à chaque valeur.
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)
Ça échoue, alors et si on soustrayait un autre vecteur ?
julia> v - [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
Ça marche, mais c'est assez fastidieux et gourmand en mémoire à mesure que les vecteurs s'allongent.
Heureusement, Julia a un point « magique » pour résoudre ce problème très simplement : v .- 0.5 est tout ce dont tu as besoin.
La section suivante explique pourquoi.
Ainsi, v - 0.5 échoue mais v .- 0.5 fonctionne, et il faut comprendre ce que fait ce point.
Deux choses, qui se combinent pour donner le résultat souhaité.
Premièrement, ajouter un point devant n'importe quel opérateur infixe signifie « appliquer cette opération à chaque élément séparément ».
De même, ajouter un point après le nom d'une fonction la « vectorise », même si cette fonction a été écrite pour des entrées scalaires.
julia> sqrt.([1, 4, 9])
3-element Vector{Float64}:
1.0
2.0
3.0
On a vu dans un exemple précédent que l'on peut soustraire des vecteurs de même longueur, mais comprends bien que .- est un opérateur plus sûr que -, parce qu'il rend explicite l'intention d'opérer élément par élément.
julia> v .- [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
Et avec des vecteurs de longueurs différentes ?
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
En général, des longueurs différentes provoquent une erreur, sauf quand l'une d'elles vaut 1 (techniquement, une dimension « singleton »).
Les singletons comme [0.5,] ou simplement 0.5 sont automatiquement étendus à la longueur nécessaire par répétition.
C'est là le cœur du broadcasting.
La sélection d'éléments d'un vecteur par leur numéro d'indice a déjà été abordée dans de précédents concepts.
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']
Il est aussi possible de sélectionner les éléments qui satisfont une certaine expression logique (techniquement, un « prédicat »). Cela nécessite généralement du 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)
Pour des expressions plus complexes, les points ont tendance à se multiplier (mais ils sont petits et faciles à taper).
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)
Pour rappel, le « vecteur » peut en fait être n'importe quel itérable ordonné approprié, comme un intervalle :
julia> n = 3:10
3:10
julia> n[isodd.(n)]
4-element Vector{Int64}:
3
5
7
9
Tu es un observateur d'oiseaux passionné qui tiens le compte du nombre d'oiseaux venus dans ton jardin ces sept derniers jours.
Tu as six tâches, qui portent toutes sur le nombre d'oiseaux ayant visité ton jardin.
Implémente la fonction today() pour qu'elle renvoie le nombre d'oiseaux qui ont visité ton jardin aujourd'hui.
Les comptages d'oiseaux sont ordonnés par jour : le premier élément correspond au comptage du jour le plus ancien, et le dernier au comptage d'aujourd'hui.
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> today(birds_per_day)
1
Implémente la fonction increment_todays_count() pour incrémenter le comptage d'aujourd'hui :
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> increment_todays_count(birds_per_day)
[2, 5, 0, 7, 4, 2]
Implémente la fonction has_day_without_birds() qui renvoie true s'il y a eu un jour où aucun oiseau n'a visité le jardin, et false sinon :
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> has_day_without_birds(birds_per_day)
true
Implémente la fonction count_for_first_days() qui renvoie le nombre d'oiseaux qui ont visité ton jardin depuis le début de la semaine, mais limite le comptage au nombre de jours spécifié depuis le début de la semaine.
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> count_for_first_days(birds_per_day, 4)
14
Certains jours sont plus chargés que d'autres.
Un jour chargé est un jour où cinq oiseaux ou plus ont visité ton jardin.
Implémente la fonction busy_days() pour qu'elle renvoie le nombre de jours chargés :
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> busy_days(birds_per_day)
2
Tu décides d'étendre tes relevés en gardant les comptages de plusieurs semaines. Dans chaque cas, les comptages sont classés par jour de la semaine, du lundi comme première entrée au dimanche comme dernière.
Implémente la fonction average_per_day() qui renvoie la moyenne sur 2 semaines.
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]
Inscris-toi sur Exercism pour apprendre et maîtriser Julia avec 35 concepts128 exercices, et un vrai mentorat humain, le tout gratuitement.