Jusqu'ici, le programme n'a pas dit grand-chose sur les types, mais de toute évidence, ils existent en Julia :
julia> vals = (42, 4.3, π, "hello", 'Q')
(42, 4.3, π, "hello", 'Q')
julia> typeof(vals)
Tuple{Int64, Float64, Irrational{:π}, String, Char}
On n'a jamais précisé les types, mais Julia les a attribués malgré tout.
types, qui sont au cœur de sa conception.Type Inference.Le compilateur JIT va parcourir (tout) le code, observer comment une variable est utilisée, et inférer un type par défaut approprié compatible avec cet usage.
Se reposer sur l'inférence de type convient pour résoudre de simples exercices de tutoriel, mais pour des programmes plus importants, tu auras probablement besoin d'un contrôle plus précis.
Sur la plupart des processeurs modernes, un entier vaut Int64 par défaut.
On a vu dans le concept Numbers qu'une valeur peut être convertie vers un type particulier autre que celui par défaut.
julia> x = Int16(42)
42
julia> typeof(x)
Int16
Cependant, la variable x peut toujours être réaffectée à un type différent :
julia> x = "changed"
"changed"
julia> typeof(x)
String
C'est l'type instability :
À la place, on peut fixer le type de x grâce à l'opérateur :: :
julia> y::Int16 = 42
42
julia> typeof(y)
Int16
julia> y = "changed"
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int16
The function `convert` exists, but no method is defined for this combination of argument types.
Désormais, y est de type Int16, et le restera toujours.
Ainsi, le compilateur sait combien d'octets réserver pour elle et peut optimiser le reste du code en s'appuyant sur un type stable.
En cela, la variable est plus ou moins semblable à celles d'un langage à typage statique comme le C.
Int64, Int16, String, Char : « d'où viennent » ces types ?
Dans de nombreux langages orientés objet (OO), chaque type est une classe, la création de sous-classes les organise en une hiérarchie de classes, et les méthodes de classe définissent les comportements.
Java et Ruby sont des exemples évidents de ce modèle, mais même Python fonctionne de façon similaire en interne.
Julia n'a pas de classes.
La raison documentée est que les fonctionnalités orientées objet interfèrent avec le compilateur JIT et nuisent aux performances à l'exécution.
Et pourtant, regarde ce code :
julia> y::Int16 = 42
42
julia> typeof(y)
Int16
julia> supertype(Int16)
Signed
julia> supertypes(Int16)
(Int16, Signed, Integer, Real, Number, Any)
Quelques précisions :
Int16 est un type, et on peut créer des variables de ce type.Int16 est un sous-type de Signed, et la fonction supertype() le montre.Integer, Real et Number jusqu'à Any au sommet, et subtypes() listera cette branche de la hiérarchie.Toutes les branches se terminent par Any, qui a la particularité d'être son propre supertype.
julia> supertypes(String)
(String, AbstractString, Any)
julia> supertype(Any)
Any
Ainsi, Julia n'a pas de hiérarchie de class, mais elle a bel et bien une hiérarchie de type.
Par la suite, on tentera de démêler comment cela fonctionne, mais il reste bien d'autres choses à explorer avant.
On peut utiliser typeof() pour tester l'égalité de la manière habituelle.
julia> typeof(11)
Int64
julia> typeof(11) == Int64
true
julia> typeof(11) == Number
false
L'égalité des types doit être exacte, car cette forme de comparaison ne tient aucun compte de la hiérarchie des types.
De façon plus souple, isa indique si une valeur a le même type qu'un comparateur, ou bien un de ses sous-types.
Il peut s'utiliser sous forme de fonction ou sous forme infixe.
julia> 12 isa Int64
true
julia> 12 isa Number
true
julia> isa(12, Number)
true
julia> 12 isa String
false
À noter que isa attend une value à gauche, et non un type.
Essayer de comparer deux types de cette façon donnera des résultats inattendus.
Le bon opérateur est <:, que l'on reverra beaucoup dans les prochains concepts.
julia> Int64 isa Number ## Don't do this!
false
julia> Int64 <: Number
true
On a vu que la hiérarchie des types forme une structure arborescente (au sens informatique, avec la racine tout en haut).
Chaque élément de l'arbre est un node, et ceux-ci peuvent être répartis en catégories :
abstract.concrete.julia> subtypes(Integer) # an abstract type
3-element Vector{Any}:
Bool
Signed
Unsigned
julia> subtypes(Int64) # a concrete type
Type[]
C'est une distinction importante, car seuls les types concrets peuvent être instantiated en tant que variables.
julia> a::Int16 = 42
42
julia> typeof(a)
Int16
julia> b::Integer = 42
42
julia> typeof(b)
Int64
À noter qu'essayer d'utiliser un type abstrait ne produit aucun message d'erreur (dans ce cas), mais le compilateur crée un type concret approprié : Int64 au lieu de Integer.
Dans cet exercice, on va faire un peu d'ingénierie de données en prétraitant des notes. Une école avec beaucoup, beaucoup, beaucoup (trop ?) d'élèves cherche à faire une analyse des notes reçues par les élèves et veut le faire efficacement, l'idée étant donc de réduire et d'organiser les données.
La notation va généralement de 0 à 10, il est donc plus efficace d'utiliser un type de données de seulement huit bits par note.
UInt8.Int8.Une fois que tu as implémenté la fonction qui convertit les notes, tu devras en écrire une autre pour traiter les collections qui les contiennent, en abaissant les notes et en les renvoyant dans l'ordre décroissant.
Vectors triés par ordre croissant pour stocker leurs notes, parce qu'ils sont méticuleux avec leurs données.Sets, qui ne sont pas triés, pour stocker leurs notes, parce qu'ils sont un peu plus paresseux.Dans les deux fonctions, tu devras gérer les entrées invalides en levant une MethodError.
La gestion des exceptions sera abordée dans un concept ultérieur, donc pour les besoins de cet exercice, tu peux utiliser la syntaxe suivante :
throw(MethodError(f, args))
Où f est la fonction et args est un tuple des arguments passés à la fonction.
Pour la fonction demote(n), f est demote et args est (n,) :
throw(MethodError(demote, (n,)))
Implémente la méthode demote(n).
Float64, elle doit arrondir à l'entier supérieur et renvoyer un type de données UInt8.Integer quelconque, elle doit renvoyer le même entier dans un type de données Int8.julia> demote(4.2)::UInt8
5
julia> demote(4)::Int8
4
julia> demote("hi")
MethodError: no method matching demote(::String) #output truncated
Implémente la méthode preprocess(coll).
Vector, elle doit abaisser tous les nombres et inverser le vecteur.Set, elle doit abaisser tous les nombres et renvoyer un vecteur trié par ordre décroissant.julia> preprocess([1, 2, 3])
3-element Vector{Int8}:
3
2
1
julia> preprocess(Set([2.2, 5.8, 3.4]))
3-element Vector{UInt8}:
6
4
3
julia> preprocess(42)
MethodError: no method matching preprocess(::Int64) #output truncated
Inscris-toi sur Exercism pour apprendre et maîtriser Julia avec 35 concepts128 exercices, et un vrai mentorat humain, le tout gratuitement.