Un tuple est une liste ordonnée finie d'éléments, qui est immuable. Les tuples exigent que toutes les positions aient un type fixe. Cela signifie donc que le compilateur sait quel type se trouve à chaque position. Les types utilisés dans un tuple peuvent être différents à chaque position, mais ils doivent être connus à la compilation.
Selon que les types des valeurs du tuple peuvent être déduits à la compilation ou non, le tuple peut être créé de différentes manières. Si les valeurs sont connues à la compilation, le tuple peut être créé à l'aide de la syntaxe littérale de tuple ; sinon, elles doivent être déclarées explicitement. Il est également important que les types des valeurs correspondent aux types spécifiés dans le tuple et que le nombre de valeurs corresponde au nombre de types spécifiés. Voici un exemple de définition par la syntaxe littérale de tuple :
tuple = {1, "foo", 'c'} # Tuple(Int32, String, Char)
Il est également possible de créer un tuple à l'aide de la classe Tuple.
tuple = Tuple(Int32, String, Char).new(1, "foo", 'c')
Autrement, tu peux spécifier explicitement le type de la variable à laquelle le tuple est affecté.
tuple : Tuple(Int32, String, Char) = {1, "foo", 'c'}
Spécifier explicitement le type du tuple peut être utile, car cela permet de définir qu'une position doit contenir un type union. Cela signifie qu'une position peut contenir plusieurs types.
tuple : Tuple(Int32 | String, String, Char) = {1, "foo", 'c'}
Tu peux créer un tuple à partir d'un tableau à l'aide de la méthode from de la classe Tuple.
Cela exige que le type du tuple soit spécifié.
array = [1, "foo", 'c']
tuple = Tuple(Int32, String, Char).from(array)
Tu peux convertir un tuple en tableau à l'aide de la méthode to_a.
Le type des éléments du tableau obtenu est l'union des types de chaque champ du tuple.
tuple = {1, "foo", 'c'}
array = tuple.to_a
array # => [1, "foo", 'c']
Comme les tableaux, les tuples sont indexés à partir de zéro, ce qui signifie que le premier élément se trouve à l'indice 0.
Cependant, contrairement aux tableaux, le type de chaque élément est fixe et connu à la compilation ; par conséquent, lorsqu'on indexe un tuple, le type de l'élément est propre à la position.
Pour accéder à un élément d'un tuple, tu peux utiliser l'opérateur [].
array = [1, "foo", 'c']
array[0] # => 1
typeof(array[0]) # => Int32 | String | Char
tuple = {1, "foo", 'c'}
tuple[0] # => 1
typeof(tuple[0]) # => Int32
Une autre différence dans l'accès aux éléments d'un tuple par rapport aux tableaux est que, si l'indice est précisé, le compilateur vérifie que cet indice se situe dans les limites du tuple. Cela signifie que tu obtiendras une erreur à la compilation plutôt qu'une erreur à l'exécution.
tuple = {1, "foo", 'c'}
tuple[3]
# => Error: index out of bounds for Tuple(Int32, String, Char) (3 not in -3..2)
Cependant, si l'indice est stocké dans une variable, le compilateur ne pourra pas vérifier à la compilation que cet indice se situe dans les limites du tuple et produira à la place une erreur à l'exécution.
Tu peux obtenir un sous-tuple d'un tuple en utilisant l'opérateur [] avec un intervalle.
Ce qui est renvoyé est un nouveau tuple contenant les éléments de l'intervalle spécifié.
L'intervalle doit être fourni à la compilation ; sinon, le compilateur ne pourra pas connaître les types des éléments du sous-tuple.
Cela signifie que l'intervalle doit être un littéral d'intervalle et non être affecté à une variable.
tuple = {1, "foo", 'c'}
subtuple = tuple[0..1] # Tuple(Int32, String)
i = 0..1
tuple[i]
# Error: Tuple#[](Range) can only be called with range literals known at compile-time
Les tuples sont utiles lorsque tu veux regrouper un nombre fixe de valeurs dont les types sont connus à la compilation. En effet, les tuples consomment moins de mémoire et sont plus rapides que les tableaux en raison de leur immuabilité. Un autre cas d'usage consiste à renvoyer plusieurs valeurs depuis une méthode. C'est particulièrement utile si les valeurs ont des types différents, puisque chaque position du tuple peut avoir un type différent.
Les tuples ne doivent pas être utilisés lorsqu'on a besoin d'une structure de données qui peut grandir ou rétrécir, ou qui doit être modifiée souvent.
Les symboles sont des identifiants nommés qui peuvent servir à faire référence à une valeur.
Les symboles sont créés à l'aide d'un littéral de symbole, c'est-à-dire en faisant précéder un nom du caractère :, par exemple :foo.
Ils peuvent aussi être écrits entre guillemets, par exemple :"foo", ce qui permet, par exemple, d'inclure des espaces dans le nom.
:foo # => :foo
:"foo boo" # => :"foo boo"
:foo == :"foo" # => true
Les symboles sont utilisés à de nombreux endroits dans le langage, notamment comme clés dans les namedtuples et pour représenter des noms de méthodes et de variables.
Les symboles en Crystal sont assez différents de ceux de Ruby. En Crystal, un symbole est une sorte de constante et il est donc affecté à la compilation. Cela signifie que les symboles ne peuvent pas être créés dynamiquement, ce qui est possible en Ruby.
En Crystal, les symboles sont représentés par des Int32, ce qui les rend très efficaces.
Les symboles peuvent être convertis en chaînes de caractères, mais pas l'inverse. En effet, les symboles sont créés à la compilation, alors que les chaînes de caractères sont créées à l'exécution.
:foo.to_s # => "foo"
Alors que tu t'apprêtes à faire des cookies pour tes amis, tu t'aperçois que tu dois convertir certaines des mesures utilisées dans la recette. Comme tu ne connais que le système métrique, tu dois trouver un moyen de convertir les mesures de cuisine américaines courantes en millilitres (mL) pour te simplifier la tâche.
Utilise ce tableau de conversion pour ta solution :
| Unité à convertir | volume | en millilitres (mL) |
|---|---|---|
| mL | 1 | 1 |
| Tasse américaine | 1 | 240 |
| Once liquide américaine | 1 | 30 |
| Cuillère à café américaine | 1 | 5 |
| Cuillère à soupe américaine | 1 | 15 |
Programmeur talentueux en formation, tu décides d'utiliser les millilitres comme unité intermédiaire pour faciliter la conversion de n'importe quelle unité de la liste vers n'importe quelle autre (y compris elle-même).
Implémente la méthode KitchenCalculator.get_volume.
Étant donné un tuple représentant une paire de volumes, elle doit renvoyer uniquement la composante numérique.
KitchenCalculator.get_volume({:cup, 2.0})
# => 2.0
Implémente la méthode KitchenCalculator.to_milliliter.
Étant donné un tuple représentant une paire de volumes, elle doit convertir le volume en millilitres à l'aide du tableau de conversion.
KitchenCalculator.to_milliliter({:cup, 2.5})
# => {:milliliter, 600.0}
Implémente la méthode KitchenCalculator.from_milliliter.
Étant donné un tuple représentant une paire de volumes et l'unité souhaitée, elle doit convertir le volume vers l'unité souhaitée à l'aide du tableau de conversion.
KitchenCalculator.from_milliliter({:milliliter, 1320.0}, :cup)
# => {:cup, 5.5}
Implémente la méthode KitchenCalculator.convert.
Étant donné un tuple représentant une paire de volumes et l'unité souhaitée, elle doit convertir le volume donné vers l'unité souhaitée.
KitchenCalculator.convert({:teaspoon, 9.0}, :tablespoon)
# => {:tablespoon, 3.0}
Inscris-toi sur Exercism pour apprendre et maîtriser Crystal avec 26 concepts142 exercices, et un vrai mentorat humain, le tout gratuitement.