Apprenez Ruby

Daniel Carrera

Ce cours présente le langage Ruby.

- Titre : Apprenez Ruby

- Auteur : Daniel Carrera

- Parution : 5 décembre 2006

- Mise à jour : 5 juillet 2020

- Licence : Permission est accordée de copier, distribuer et/ou modifier ce document selon les termes de la Licence de Documentation Libre GNU (GNU Free Documentation License), version 1.1 ou toute version ultérieure publiée par la Free Software Foundation, sans Sections Invariables, sans Texte de Première de Couverture, et sans Texte de Quatrième de Couverture. Une copie de la présente Licence est incluse ici.

Sommaire

- Introduction

- Remerciements

- I. Les bases

- I-A. Premiers pas

- I-A-1. Utiliser Ruby comme une calculatrice

- I-A-2. Les nombres et Ruby

- I-A-2-a. Entiers

- I-A-2-b. Décimaux

- I-A-3. Autres opérateurs arithmétiques

- I-A-4. Nombres énormes, et nombres minuscules

- I-A-5. Quelques exercices

- I-B. Chaines de caractères

- I-B-1. Opérateurs

- I-B-2. Méthodes

- I-B-3. Quelques exercices

- I-C. De nouveaux noms pour de vieilles choses

- I-C-1. Classes et objets

- I-C-2. La notation Classe#méthode

- I-C-3. Convertir une classe

- I-C-4. Convertir une classe

- I-D. Variables

- I-D-1. Travailler avec des variables

- I-D-2. Raccourcis

- I-D-3. Constantes

- I-D-4. Quelques exercices

- I-E. Votre premier programme

- I-E-1. Bonjour monde

- I-E-2. Un autre exemple

- I-E-3. Créer des programmes exécutables

- I-E-4. Quelques exercices

- I-F. Écrire de bons programmes

- I-F-1. Noms de variables

- I-F-1-a. Noms significatifs

- I-F-1-b. Noms composés

- I-F-2. Travailler avec IRB

- I-F-3. Utiliser des constantes

- II. Contrôle de l'exécution

- II-A. Boucles

- II-A-1. Compter

- II-A-2. Somme de nombres

- II-A-3. Étaler une expression sur plusieurs lignes

- II-A-4. Plus d'exemples

- II-A-4-a. Compter à l'envers

- II-A-4-b. Travailler sur une variable un certain nombre de fois

- II-A-4-c. Quelques exercices

- II-B. Saisir des données provenant de l'utilisateur

- II-B-1. Ce fichu «\n»!

- II-B-2. Retour à notre programme

- II-B-3. Quelques exercices

- II-C. Conditions

- II-C-1. Vrai ou faux

- II-C-2. Opérateurs conditionnels

- II-C-3. Comparaison de chaines de caractères

- II-C-4. L'instruction elsif

- II-C-5. Un exemple: prix.rb

- II-C-6. Quelques exercices

- II-D. Les boucles while

- II-D-1. Compteur

- II-D-2. Puissance de 2

- II-D-3. Quelques exercices

- II-E. Écrire de bons programmes

- II-E-1. Commentaires

- II-E-1-a. Comment utiliser des commentaires

- II-E-2. Indentation

- III. Structures de données

- III-A. Tableaux : introduction

- III-B. Que peuvent faire les tableaux?

- III-B-1. Array#sort

- III-B-2. Array#reverse

- III-B-3. Array#length

- III-B-4. Opérations arithmétiques sur des tableaux

- III-B-5. Afficher le contenu d'un tableau

- III-B-6. Quelques exercices

- III-C. Itérateurs

- III-C-1. Quelques exercices

- III-D. Hachages

- III-D-1. Quelques définitions : clef et valeur

- III-D-2. Hachages et itérateurs

- III-D-2-a. Hash#each

- III-D-2-b. Hash#each_key

- III-D-2-c. Hash#each_value

- III-E. Exemple pratique : un carnet d'adresses

- III-E-1. Première étape : analyse

- III-E-2. Deuxième étape : adresses

- III-E-3. Troisième étape : personnes

- III-E-4. Quatrième étape : carnet d'adresses

- III-E-5. Quelques exercices

- III-F. Afficher le contenu du carnet

- III-F-1. Afficher des structures de données complexes

- III-F-2. Noms complets

- III-F-3. Numéros de téléphone

- III-F-4. Adresses

- III-G. Trier les éléments du carnet

- III-G-1. Retour sur Array#sort

- III-G-2. Retourner des valeurs

- III-G-3. L'opérateur <=>

- III-G-4. Trier sur le nom de famille

- III-G-5. Trier le carnet d'adresses

- III-G-6. Quelques exercices

- III-H. Écrire de bons programmes

- III-H-1. Tableau ou hachage ?

- III-H-2. Noms de variables

- III-H-2-a. Tableaux

- III-H-2-b. Hachages

- III-H-3. Commentaires

- III-H-4. Sous-structures

- III-H-4-a. Bien

- III-H-4-b. Peut mieux faire

- III-H-4-c. Pas bien

- IV. Classes et méthodes

- IV-A. Fonctions

- IV-A-1. Qu'est-ce qu'une fonction ?

- IV-A-2. « Bonjour monde » avec une fonction

- IV-A-3. Paramètres de fonction

- IV-A-4. Afficher une adresse

- IV-B. Classes et méthodes

- IV-B-1. Lire les données d'un objet

- IV-B-2. Modifier les données d'un objet

- IV-B-3. Accéder à des données

- IV-C. Plus de classes

- IV-C-1. Une classe Personne

- IV-C-2. Afficher le contenu d'une classe

- IV-C-3. Quelques exercices

- IV-D. Implémentation du carnet d'adresses

- IV-D-1. Première étape

- IV-D-2. Tri automatique

- IV-D-2-a. Comment trier un tableau?

- IV-D-2-b. Simplification

- IV-D-2-c. Finalement

- IV-E. Écrire des itérateurs

- IV-E-1. Exécuter un bloc de code

- IV-E-2. Passage de paramètres

- IV-E-3. Implémentation de Carnet#chaque_personne

- IV-E-4. Implémentation de Carnet#chaque_adresse

- IV-E-5. Code complet de la classe Carnet

- IV-F. Autres fonctionnalités

- IV-F-1. Méthodes publiques et privées

- IV-F-2. Réutilisation du code avec require

- IV-G. Écrire de bons programmes

- IV-G-1. Fonctions et méthodes

- IV-G-2. Commentaires

- V. Obtenir de l'aide

- V-A. Support technique

- V-B. Documentation

- V-B-1. Autres tutoriels

- V-B-2. Livres

- VI. Installer Ruby sur votre machine

- VI-A. GNU/Linux

- VI-A-1. Installation par RPM (Redhat, Mandrake…)

- VI-A-2. Gentoo

- VI-A-3. Debian

- VI-B. Mac OS X

- VI-C. FreeBSD

- VI-D. Microsoft Windows

Introduction

Ce tutoriel va vous permettre de vous familiariser avec le langage Ruby. Il est avant tout destiné aux débutants. Ces derniers pourront, sans posséder aucune connaissance au préalable, se servir de ce tutoriel comme tremplin pour leur long apprentissage du meilleur langage de programmation du monde!

Ce tutoriel est protégé par la License de documentation libre GNU. Ce qui signifie entre autres qu'il vous est permis de le redistribuer gratuitement, ainsi que de le modifier à votre guise (tout en respectant les conditions de la licence).

N'hésitez pas à contacter notre liste de diffusion si vous avez des questions supplémentaires, des suggestions, des critiques… à propos de ce document. Déposez simplement un e-mail à l'adresse ruby-tut-developer@nongnu.org.

Remerciements

Le traducteur tient à remercier les personnes suivantes, sans lesquelles l'élaboration de ce document n'aurait été possible :

- Eric Landuyt ;

- Laurent Mouillart ;

- Philippe Lesage ;

- Stéfanie Dijoux.

I - Les bases

À propos de ce chapitre

Ce chapitre se focalise sur les bases fondamentales nécessaires afin que vous puissiez écrire votre premier programme en Ruby. Il est donc indispensable pour toute personne qui veut programmer en Ruby de lire ce chapitre.

I-A - Premiers pas

Nous allons commencer notre exploration du langage Ruby en utilisant l'invite de commande interactif IRB (en anglais, « interactive ruby shell »). Pour ce faire, ouvrez un terminal et entrez :

irb --simple-prompt

Vérifiez que le programme IRB démarre correctement avant de continuer.

I-A-1 - Utiliser Ruby comme une calculatrice

L'interpréteur interactif IRB peut s'utiliser facilement comme calculatrice :

Ruby est capable d'évaluer toutes les opérations arithmétiques de base :

Symbole

Effet

+

addition

-

soustraction

*

multiplication

/

division

Pour quitter le programme IRB, il suffit d'entrer exit :

Nous allons maintenant jouer un peu plus avec les opérations arithmétiques de base. Essayez ceci :

Remarquez bien le comportement de Ruby lorsque nous essayons de diviser 3 par 2 :

Que s'est-il passé ? Rien d'anormal, je vous rassure! Ruby nous montre simplement qu'il travaille avec deux classes différentes de nombres :

- entiers ;

- décimaux (appelés aussi « flottants »).

I-A-2 - Les nombres et Ruby

I-A-2-a - Entiers

Un entier est un nombre comme 1, 2, -5, etc. Lorsque nous travaillerons avec des entiers, Ruby nous donnera toujours une réponse mathématiquement exprimée sous forme entière.

3 / 2 devrait nous donner 1.5, mais comme cette valeur n'est pas entière et que les deux nombres utilisés dans l'opération sont entiers, Ruby renverra donc à la place une réponse formulée sous forme entière, c'est-à-dire 1.

I-A-2-b - Décimaux

Un nombre décimal est souvent appelé «flottant», par analogie avec sa partie décimale. 3.14, 1.5 et 3.0 sont des flottants. Ruby renverra toujours une valeur flottante comme résultat pour des opérations arithmétiques agissant sur des flottants. Par exemple :

I-A-3 - Autres opérateurs arithmétiques

Avant d'en finir avec ce chapitre, jetons un petit coup d'œil à deux autres opérateurs arithmétiques :

Symbole

Effet

**

exposant

%

modulo (le reste d'une division)

Notez bien le comportement de l'opérateur % avec les nombres décimaux. Dans cet exemple, 2 rentre deux fois dans 5.1, et il reste 1.1 de trop.

I-A-4 - Nombres énormes, et nombres minuscules

Ruby convient très bien lorsqu'il s'agit de travailler avec de très grands et de très petits nombres. Supposez par exemple que vous souhaitez enregistrer quelque part le nombre 192349562563447.

Bon, c'est loin d'être facile à lire. Dans la vie courante, nous sommes habitués à séparer les chiffres par des caractères spéciaux, en fonction du pays d'origine (il s'agit souvent d'une virgule, d'un point ou d'un espace blanc). Par exemple, nos amis anglo-saxons auraient représenté ce fameux nombre de cette façon : 192,349,562,563,447. Ruby fonctionne exactement de la même façon, en utilisant des « blancs soulignés » (en anglais, « underscores ») :

Comment faire si vous désirez travailler avec des nombres énormes comme 17_000_000_000_000_000_000, ou des nombres minuscules comme 0.000_000_000_000_321? En temps normal, la méthode la plus simple (et la plus paresseuse) consiste à utiliser une notation scientifique en écrivant 1.7 x 1019, et 3.21 x 10-13. Et comme vous vous en seriez douté, Ruby permet évidemment d'utiliser cette notation :

I-A-5 - Quelques exercices

1. Calculez le nombre d'heures contenues dans année.

2. Calculez le nombre de minutes dans une décennie.

3. Quel est votre âge en secondes ?

4. Que vaut l'expression 3.24 * ((34/2) - 54)/33.4 * 3.4 ? (notez qu'il est possible d'utiliser des parenthèses)

5. Quel sera le résultat d'une opération qui combine à la fois des flottants et des entiers ? Par exemple, calculez ceci : 3.0 / 2 ; 3 / 2.0 ; 4 ** 2.0 ; 4.1 % 2

La réponse sera-t-elle donnée sous forme entière ou décimale ?

I-B - Chaines de caractères

Les nombres ne sont pas les seuls types de données que vous utiliserez pour programmer avec Ruby. En effet, vous allez probablement devoir manipuler des lettres, des mots ou encore des blocs de texte.

Ruby identifie ce genre de données par le terme chaine de caractères (en anglais, « string »). Voici quelques exemples de chaines

- « a »

- « Salut. »

- « Longue vie à Ruby !!! »

- « 5 est mon nombre préféré. Quel est le vôtre? »

- « Snoopy a dit: #%^?*@! »

I-B-1 - Opérateurs

Voici quelques trucs amusants que Ruby vous permet de faire avec des chaines de caractères :

Voici un moyen de s'en souvenir : « salut » * 3 fait la même chose que « salut » + « salut » + « salut » , ce qui nous donnera donc « salut salut salut ».

Remarquez ce qu'il se passe lorsque nous retirons l'espace blanc de « salut » :

Maintenant, regardez ceci :

Ruby considère « 1 » comme étant une chaine de caractères, pas un nombre.

I-B-2 - Méthodes

Voici quelques méthodes utiles qui travaillent avec des chaines de caractères :

I-B-3 - Quelques exercices

Quel sera le résultat de l'expression suivante : 'Bonjour '.length + 'monde!'.length ?

I-C - De nouveaux noms pour de vieilles choses

Ruby identifie par des noms spéciaux certaines choses que nous connaissons déjà. Par exemple, il utilise le mot Float («flottant») pour représenter un nombre décimal. Voici quelques autres définitions :

- Object (« objet ») : Ce n'est rien d'autre qu'un bout de données. Par exemple, le nombre 3 ou la chaine de caractères « Salut! » sont des objets ;

- Class (« classe ») : Ruby sépare toujours les types de données en classes. Par exemple, les entiers, les flottants et les chaines de caractères sont représentés par des classes différentes ;

- Method (« méthode ») : Les méthodes sont des bouts de code qui peuvent travailler sur des objets. Par exemple, l'opérateur + (qui permet d'additionner deux objets, souvent des entiers ou des chaines) est bel et bien une méthode.

Voici la liste des classes Ruby associées aux types de données que vous venez de découvrir :

Type de donnée

Classe associée

nombre entier

Integer

nombre décimal

Float

texte

String

Souvenez-vous, vous avez également utilisé plusieurs méthodes associées à ces classes :

Classe

Quelques méthodes

Integer

+, -, /, *, %, **

Float

+, -, /, *, %, **

String

capitalize, reverse, length, upcase, +, *

I-C-1 - Classes et objets

Soyez certain de bien comprendre la différence entre une classe et un objet. Un objet, c'est une donnée quelconque. Une classe, c'est la structure de l'objet, ce à quoi il ressemble, ce qui le différencie des autres objets.

Par exemple, 3 et 5 sont des nombres différents. Ce sont donc des objets différents. Mais ce sont tous deux des nombres entiers, donc ils appartiennent à la même classe («Integer»). Voici quelques exemples :

Objet

Classe

2, -5

Integer

7.2, 3.14

Float

« Vive », « Ruby »

String

I-C-2 - La notation Classe#méthode

Souvenez-vous, différentes classes peuvent avoir différentes méthodes. Voici quelques différences que nous avons déjà remarquées dans ce chapitre :

- l'opérateur de division (/) ne fonctionne pas de la même façon avec des entiers et des flottants ;

- l'opérateur d'addition (+) se comporte différemment avec des nombres et des chaines de caractères. Il peut servir à calculer le résultat de l'addition de deux nombres, et il peut également concaténer deux chaines de caractères ;

- les chaines de caractères possèdent plusieurs méthodes que les entiers et les flottants n'ont pas. Par exemple : capitalize, length, upcase, etc..

Pour cette raison, nous utiliserons la notation Classe#méthode pour identifier exactement de quelle méthode nous parlerons. Par exemple, nous dirons Integer#+ afin de différencier cette méthode de Float#+ et de String#+. Nous pouvons également affirmer que String#upcase existe, mais que Integer#upcase n'existe pas.

I-C-3 - Convertir une classe

Voici quelques méthodes permettant d'effectuer des conversions pour les trois classes que nous venons d'étudier :

Méthode

Classe de départ

Classe d'arrivée

String#to_i

Chaine de caractères

Entier

String#to_f

Chaine de caractères

Flottant

Float#to_i

Flottant

Entier

Float#to_s

Flottant

Chaine de caractères

Integer#to_f

Entier

Flottant

Integer#to_s

Entier

Chaine de caractères

Un petit exemple pratique avec notre ami IRB :

I-C-4 - Convertir une classe

Vous pouvez demander à Ruby si un objet est issu d'une classe particulière. Essayez ceci dans l'interpréteur :

12.is_a?(Integer)
12.is_a?(Float)
12.is_a?(String)

'12'.is_a?(Integer)
'12'.is_a?(Float)
'12'.is_a?(String)

12.0.is_a?(Integer)
12.0.is_a?(Float)
12.0.is_a?(String)

Essayez également ceci :

12 + 12
'12' + '12'

'12'.to_i + 12
'12' + 12.to_s

12 * 12
'12' * 12

Avez-vous obtenu les résultats que vous espériez ?

Comment expliqueriez-vous la différence entre 12, '12' et 12.0 à un enfant ?

I-D - Variables

Ruby vous permet d'utiliser des variables pour associer des noms à des objets particuliers. Exemple

ville = "Toronto"

Ici, Ruby associe la chaine de caractères « Toronto » à la variable ville.

Si vous avez des difficultés, imaginez-vous Ruby fabriquant deux tableaux. Un contenant les objets, et un autre contenant les noms qui leur sont associés. Ensuite, imaginez-vous Ruby dessinant une flèche de ville à « Toronto ».

Lorsque Ruby rencontrera la variable ville, il suivra logiquement la flèche et arrivera sur la chaine de caractères « Toronto ».

Les noms de variables doivent toujours commencer par une minuscule!

I-D-1 - Travailler avec des variables

Vous pouvez manipuler des variables exactement de la même façon que vous auriez manipulé les objets qu'elles représentent. Voici un exemple :

L'avantage des variables, c'est que vous pouvez garder quelque part une trace de vos données facilement. Imaginez par exemple que l'on vous donne les instructions suivantes :

1. Additionnez ensemble 2, 4, 6 et 8 ;

2. Prenez le résultat, et divisez-le par 5 ;

3. Calculez le produit simultané de 2, 3 et 4 ;

4. Prenez le résultat obtenu à la ligne 2, et soustrayez-y ce que vous venez d'obtenir à la ligne 3.

Bien entendu, vous pouvez écrire une longue expression pour calculer ceci. Mais il est évidemment plus facile d'écrire :

I-D-2 - Raccourcis

Dans l'exemple ci-dessus, nous avons vu les expressions :

x = x / 5
y = y - x

On rencontre assez souvent ce genre d'expressions, donc Ruby (qui pense à nous) nous permet d'utiliser les raccourcis suivants :

Exemple

Raccourci

Effet

x = x + 2

x += 2

Additionner 2 à x

x = x - 3

x -= 3

Soustraire 3 de x

x = x * 6

x *= 6

Multiplier x par 6

x = x / 2

x /= 2

Diviser x par 2

x = x**3

x **= 3

Exposer x au cube

x = x % 4

x %= 4

Calculer le reste de la division de x par 4, et y enregistrer le résultat

Donc, notre exemple du dessus aurait pu être écrit de cette façon :

I-D-3 - Constantes

Les constantes ressemblent à des variables, à la différence près que vous informez Ruby que sa valeur est supposée ne pas changer. Si vous essayez de modifier la valeur d'une constante, Ruby vous enverra un avertissement.

Vous pouvez définir des constantes de la même façon que des variables, sauf que leur nom doit commencer par une majuscule.

Même si Ville est une constante, sa valeur change quand même. Définir une constante signifie seulement que Ruby vous avertira si vous modifiez sa valeur. Regardez :

I-D-4 - Quelques exercices

Pensez-vous que les raccourcis fonctionnent également avec les chaines de caractères? Essayez ceci :

var = "Bonjour "
var += "monde"

À votre avis, que va donner ce code :

var = "salut"
var *= 3

Essayez-le. Comment auriez-vous expliqué le résultat à un enfant ?

I-E - Votre premier programme

Félicitations! Vous êtes maintenant prêt à commencer à écrire des programmes en Ruby.

I-E-1 - Bonjour monde

Ouvrez votre éditeur favori, et entrez-y la ligne suivante :

puts "Bonjour monde"

Sauvez le fichier sous le nom de bonjour.rb et démarrez-le en invoquant

ruby bonjour.rb

puts est une méthode qui affiche une chaine de caractères sur le terminal.

Voici un nouvel exemple :

Regardez bien ce que nous avons fait. nom est une chaine de caractères. Donc, nous pouvons la concaténer à d'autres chaines de caractères, comme nous avons vu précédemment.

Dans un programme Ruby, seules les lignes passées à puts seront affichées à l'écran.

I-E-2 - Un autre exemple

Souvenez-vous, dans la section précédente, nous avions entré ceci dans IRB :

Nous allons créer un nouveau programme Ruby à partir de ce code. Copiez simplement les lignes dans un nouveau fichier, et rajoutez-y à la fin la ligne suivante :

puts y

Ensuite, sauvez le fichier et invoquez Ruby :

Ça fonctionne! Cependant, nous aimerions modifier un peu le message de sortie. L'idéal serait que le programme affiche «La réponse est 20» à l'écran. On peut toujours essayer ceci :

puts "La réponse est " + y   # provoquera une erreur

Ça ne marchera évidemment pas. Pourquoi? Souvenez-vous, seules des chaines peuvent être concaténées à d'autres chaines. Nous devons donc convertir notre entier y en chaine de caractères. Nous savons qu'il faut utiliser la méthode Integer#to_s.

I-E-3 - Créer des programmes exécutables

Si vous travaillez sur un système compatible UNIX (Linux, *BSD, Solaris, Cygwin + Win32…), vous pouvez rendre vos programmes Ruby exécutables. Ils pourront donc être démarrés de la même façon que les autres programmes.

Premièrement, nous devons savoir où se cache Ruby sur votre machine. Pour ce faire, entrez la commande which ruby dans un terminal :

Recopiez le chemin sur la toute première ligne de votre programme. N'oubliez pas de préfixer la ligne par #!.

Il ne reste plus qu'à spécifier l'attribut du programme, ce que fera la commande chmod +x prog.rb (+x signifie «exécutable») :

I-E-4 - Quelques exercices

Transformez en programme les exercices vus dans la section précédente.

Complétez le code suivant :

nom = "Laurent"
âge = 22

Le programme doit afficher « Laurent a 22 ans ».

I-F - Écrire de bons programmes

Beaucoup trop de tutoriels vous enseignent simplement comment programmer, sans vous apprendre comment faire de bons programmes.

I-F-1 - Noms de variables

Voici certaines choses dont vous devriez tenir compte lors du choix des noms de variables.

I-F-1-a - Noms significatifs

Le nom d'une variable doit permettre de deviner le type de l'information contenue dans l'objet. Voici quelques exemples :

Bien

Pas bien

age

a

etudiant

foo

nom

xwy

somme

truc

I-F-1-b - Noms composés

N'ayez surtout pas peur de choisir des noms de variables composés de plus d'un mot. Soyez simplement sûr que le nom de la variable est lisible.

Imaginez que vous désirez utiliser une variable pour enregistrer l'âge d'un étudiant. Il existe deux conventions :

- ageEtudiant ;

- age_etudiant.

Nous préférons la dernière possibilité. Mais c'est à vous de choisir.

N'écrivez pas ageetudiant. Vous n'auriez probablement pas apprécié si nous avions écrit ce tutoriel sans espacer les mots.

I-F-2 - Travailler avec IRB

Vous ne devez pas arrêter d'utiliser IRB maintenant que vous savez comment utiliser votre éditeur pour écrire des programmes Ruby. Nous vous avons montré IRB tout au début pour une bonne raison. Lorsque vous programmerez avec Ruby, vous devriez toujours garder un terminal ouvert avec IRB, et y retourner pour expérimenter vos idées.

IRB a été conçu dans cette optique d'utilisation. Si vous l'utilisez judicieusement, vous deviendrez un bon programmeur Ruby.

I-F-3 - Utiliser des constantes

Utilisez toujours des constantes pour représenter des valeurs fixes dans vos programmes. Il s'agit d'une bonne précaution, car Ruby vous aidera à identifier des erreurs potentielles dans votre code.

Quelques exemples :

Pi = 3.14159265
Masse_electron  = 9.109e-31
Vitesse_lumiere = 3e8
Distance_terre_soleil = 5.79e10

Essayez également d'éviter d'écrire directement des valeurs numériques. Utilisez des constantes pour rendre votre code plus lisible. Par exemple, la formule pour calculer l'aire d'un cercle est la suivante :

aire = pi . r²

r = rayon

Votre code doit se rapprocher au maximum de la formule. Voici ce qu'il faut faire, et ce qu'il ne faut pas faire :

Bien

Pas bien

PI = 3.14159265 aire = PI * (rayon ** 2)

aire = 3.14159265 * (rayon ** 2)

II - Contrôle de l'exécution

À propos de ce chapitre

Ce chapitre aborde les concepts fondamentaux requis pour effectuer n'importe quelle tâche avec Ruby. Vous devrez probablement traiter des données provenant de l'utilisateur, ou exécuter une tâche plusieurs fois. Tout ceci sera introduit dans ce chapitre.

II-A - Boucles

Dans cette section, nous allons vous initier à un des aspects les plus puissants de la programmation : les boucles.

Démarrez votre éditeur favori, et entrez-y les lignes suivantes :

Pouvez-vous deviner ce que fera ce bout de code? Sauvegardez le fichier sous le nom boucles.rb et exécutez-le :

Comme vous avez pu le constater, le contenu de la boucle a été exécuté 4 fois. Il s'agit probablement du moyen le plus direct que Ruby nous offre pour reproduire des tâches.

II-A-1 - Compter

Nous allons ici nous servir de ce que nous venons d'apprendre sur Ruby et les variables pour afficher les nombres compris entre 1 et 5 :

Souvenez-vous, la méthode Integer#to_s se charge de convertir la valeur numérique représentée par l'objet en une chaine de caractères. Nous avons besoin de cette méthode pour pouvoir concaténer le résultat à la chaine « count = ».

Lors de l'exécution, vous devriez avoir ceci :

II-A-2 - Somme de nombres

Supposons que nous devons calculer la somme de tous les nombres compris entre 1 et 11. Nous venons juste de découvrir comment obtenir les nombres compris entre 1 et 11. Il nous reste simplement à les additionner :

Et voici ce qui s'affichera à l'écran :

II-A-3 - Étaler une expression sur plusieurs lignes

Dans le dernier exemple, l'expression puts devenait quand même longue. Que faire si nous devions afficher une ligne deux, trois, ou quatre fois plus longue ?

Vous pouvez couper l'expression en plusieurs lignes, en terminant chaque ligne par le caractère «backslash» ('\').

Le caractère «backslash» doit absolument être le dernier caractère de la ligne. Si vous rajoutez un espace après, vous obtiendrez une erreur.

Essayez ceci dans IRB :

La ligne « =>nil » signifie simplement que la méthode puts ne renvoie rien.

En fait, ce n'est pas tout à fait vrai. puts renvoie quand même une valeur qui représente un état non initialisé. Ruby représente cet état particulier par l'expression nil.

Si nous avions écrit ceci :

etat = puts "Salut"

La variable etat aurait contenu nil.

Nous venons donc de voir comment étaler notre commande puts sur deux lignes. Mettons donc en pratique (dans notre programme) ce que nous venons d'apprendre :

En effet, nous pouvons utiliser cette technique pour afficher autant de lignes que nous désirons :

Vous n'êtes évidemment pas obligé de présenter la sorte du programme de cette façon. Cependant, nous pensons que l'utilisateur comprendra mieux le fonctionnement du programme. Voici ce que vous pouvez obtenir lors de son exécution :

II-A-4 - Plus d'exemples

Nous allons ici vous montrer deux autres exemples à propos des boucles dans Ruby.

II-A-4-a - Compter à l'envers

Et si nous essayions de compter à l'envers? Ce concept peut se révéler fort utile, imaginez que la Nasa vous demande d'écrire un programme de décomptage pour leurs fusées! Essayez de copier ceci dans un nouveau programme :

Et voici le résultat fort attendu (notez que nous ne sommes pas responsables de l'explosion de votre ordinateur) :

II-A-4-b - Travailler sur une variable un certain nombre de fois

Évidemment, nous pouvons utiliser des boucles pour effectuer plusieurs fois une même opération sur une même variable.

Voici un exemple pratiquement valable. Il s'agit de calculer la factorielle d'un nombre.

La factorielle d'un nombre est le produit de tous les nombres compris entre 1 et ce nombre. Vous me suivez ? Par exemple, voici l'expression pour calculer la factorielle du nombre 6 :

6! = 6 * 5 * 4 * 3 * 2 * 1 = 720

Les mathématiciens utilisent le symbole n! pour représenter la factorielle du nombre n

Tapez ceci dans un nouveau programme Ruby, et démarrez-le :

II-A-4-c - Quelques exercices

- Que vaut la somme des entiers compris entre 1 et 1000 ?

- Que vaut la somme des entiers compris entre 10 et 100 ?

- Écrivez un programme qui affiche les paroles de la chanson :

1 kilomètre à pied, ça use, ça use
1 kilomètre à pied, ça use, ça use les souliers

La peinture à l'huile, c'est bien difficile
Mais c'est bien plus beau
Que la peinture à l'eau

2 kilomètres à pied...

(arrêtez-vous à 100 kilomètres)

II-B - Saisir des données provenant de l'utilisateur

Dans cette section, nous allons écrire un programme qui salue l'utilisateur. Le programme lui demandera son nom avant de le saluer. La méthode pour lire une chaine de caractère venant de l'utilisateur s'appelle gets.

Sauvez le fichier et démarrez-le :

Que s'est-il passé ? Pourquoi ce fichu programme va-t-il à la ligne ?

La réponse est évidente : tout simplement parce que vous avez entréune nouvelle ligne au clavier. Oui, souvenez-vous, quand vous avez appuyé sur la touche Enter…

Nous allons regarder ceci de plus près dans IRB. Démarrez IRB et tapez l'expression nom = gets. L'ordinateur va attendre une saisie, entrez une chaine quelconque. Regardez attentivement ce qu'il va se passer :

Que signifie donc ce «\n» à la fin de la chaine de caractères ?

Ce «\n» représente un retour à la ligne. Il s'agit du caractère envoyé par le clavier lorsque l'utilisateur appuie sur la touche Enter.

Avez-vous remarqué comment nous venons d'utiliser IRB pour comprendre ce qu'il s'était passé ? Il arrivera tôt ou tard que votre code se comporte d'une façon anormale. À ce moment-là, essayez-le dans IRB, vous comprendrez plus facilement la source de vos ennuis !

II-B-1 - Ce fichu «\n»!

OK, nous savons maintenant ce qui ne va pas. Mais comment résoudre ce problème?

Il existe précisément une méthode qui s'occupera de retirer le caractère «\n» d'une chaine de caractères : String#chomp. Retournons dans IRB pour tester directement cette méthode :

Magnifique! La méthode String#chomp nous renvoie la chaine de caractères sans le retour à la ligne. Nous pouvons donc maintenant écrire :

Notez bien que la variable nom contient toujours le saut à la ligne. La bonne variable (sans le «\n») est nouveau_nom. Mais pourquoi utiliser une autre variable? Pourquoi ne pas simplement écrire ceci :

>>nom = nom.chomp

De cette façon, nous retirons directement le saut à la ligne de la variable nom.

II-B-2 - Retour à notre programme

Il est maintenant temps de corriger le problème dans notre code :

Une dernière petite réflexion :

1. chomp est une méthode de la classe String (souvenez-vous de la notation : String#chomp) ;

2. gets nous renvoie une chaine de caractères (précisément, un objet de la classe String !).

Pourquoi ne pas écrire gets.chomp ? De cette façon, String#chomp sera appelé sur ce que gets retournera. En d'autres termes, nous pouvons écrire :

N'est-ce pas joli? Mettez à jour votre code et invoquez Ruby :

II-B-3 - Quelques exercices

- Écrivez un programme qui capture deux mots du clavier, et qui les imprime sur l'écran dans l'ordre opposé de leur saisie.

- Écrivez un programme dont le comportement devra ressembler à ceci :

Pour calculer l'année de naissance de l'utilisateur, effectuez une simple soustraction entre l'année courante et son âge.

- Écrivez un programme qui demandera à l'utilisateur un nombre ainsi qu'une chaine de caractères. Ensuite, le programme doit afficher autant de fois la chaine inversée. Exemple :

Il existe plusieurs solutions possibles à cet exercice. Choisissez celle que vous préférez.

- Devinez ce que produira Ruby avec cette ligne :

nombre = gets.chomp.to_i

Essayez-la dans IRB.

- Écrivez un programme qui calculera et affichera la factorielle d'un nombre entré au clavier par l'utilisateur. Il devra fonctionner comme ceci :

Vous pouvez réutiliser le code que nous avons écrit au chapitre précédent.

II-C - Conditions

Dans Ruby, il est possible d'effectuer plusieurs actions en fonction d'une série de conditions, en utilisant le mot magique if. Voici un exemple :

#1
if ville == "Toronto"
age_legal_alcool = 19
#2
else
age_legal_alcool = 21
end

Ce qui veut dire :

- #1 Si (if) le contenu de la variable ville est égale à (==) la chaine « Toronto », alors nous affectons à la variable age_legal_alcool le nombre 19 ;

- #2 Sinon (else), nous affectons à la variable age_legal_alcool le nombre 21.

II-C-1 - Vrai ou faux

Ruby permet de distinguer deux états différents d'une expression :

- vrai (true) ;

- faux (false).

Le meilleur moyen d'illustrer cette théorie est d'expérimenter quelques exemples. Comme toujours, démarrez IRB et entrez les commandes suivantes :

L'instruction if évalue si une expression quelconque (par exemple, ville == « Toronto ») est soit vraie (true), soit fausse (false) et ensuite agit en conséquence.

Notez bien la différence entre les opérateurs = et == :

- = est un opérateur d'affectation (il permet d'assigner une valeur à une variable)

- == est un opérateur de comparaison (il permet de comparer l'état de deux expressions)

II-C-2 - Opérateurs conditionnels

Voici une liste regroupant la plupart des opérateurs conditionnels :

Opérateur

Effet

==

égal à

!=

n'est pas égal à

>

est plus grand que

<

est plus petit que

>=

est plus grand ou égal à

<=

est plus petit ou égal à

II-C-3 - Comparaison de chaines de caractères

Comment ces opérateurs se comportent-ils avec des chaines de caractères? L'opérateur == vérifie si deux chaines sont exactement identiques. Les autres opérateurs se basent sur la table ASCII pour produire et renvoyer un état de comparaison.

Qu'est-ce que cette table ASCII? Il s'agit d'une table contenant tous les caractères pouvant être générés par le clavier. Ces caractères sont triés dans cet ordre :

1. D'abord les chiffres (012…789) ;

2. Ensuite les lettres majuscules (ABC…XYZ) ;

3. Finalement les lettres minuscules (abc…xyz).

ASCII: « American Standard Code for Information Interchange ». Ce code permet d'échanger des informations entre des ordinateurs construits par différentes entreprises. Il a été conçu pour la langue anglaise à la base. Néanmoins, il ne convient pas pour les autres langues utilisant des caractères spéciaux (comme le japonais, par exemple). Pour remédier à ce problème, Ruby utilise un système d'encodage nommé UTF8, qui permet de combiner des caractères ASCII (codés sur un octet) et des symboles spéciaux (codés sur deux octets).

Maintenant, démarrez IRB et entrez ceci :

Avez-vous remarqué comment IRB peut clarifier les choses? Vous devriez vous habituer à essayer d'abord certaines idées dans IRB. C'est un outil génial, utilisez-le !

II-C-4 - L'instruction elsif

L'instruction elsif vous permet de rajouter plus d'une condition dans votre expression. Prenez ceci par exemple :

#1
if âge >= 60
puts "Prix senior"
#2
elsif age >= 14
puts "Prix adulte"
#3
elsif âge >= 2
puts "Prix enfant"
#4
else
puts "Gratuit!"
end

Essayons de comprendre ceci :

- #1 Si la variable âge contient une valeur plus grande ou égale à 60, nous donnons un prix senior.

- #2 Si ce n'est pas vrai, mais que âge est plus grand ou égal à 14, nous donnons un prix adulte.

- #3 Si ce n'est pas vrai, mais que âge est plus grand que 2, nous donnons un prix enfant.

- #4 Sinon, c'est gratuit !

Ruby exécutera cette séquence de conditions une par une. Uniquement la première condition vraie sera exécutée. Vous pouvez rajouter autant de elsif que vous désirez.

II-C-5 - Un exemple: prix.rb

Pour rendre les choses plus simple à comprendre, nous allons rajouter ces conditions dans un programme. Il devra demander l'âge de l'utilisateur, et ensuite afficher le prix correspondant à cet âge :

Ce programme devrait se comporter comme ceci :

Soyez bien conscient de l'ordre dans lequel vous rajoutez vos conditions avec elsif. Seulement le code associé à la première condition évaluée comme étant «vraie» sera exécuté. Cet exemple illustre bien ce danger :

age = 21

if âge >5
puts "Plus vieux que 5 ans"
elsif âge >10
puts "Plus vieux que 10 ans"
elsif âge >20
puts "Plus vieux que 20 ans"
elsif âge >30
puts "Plus vieux que 30 ans"
end

En lisant ce code rapidement, il semblerait logique que le programme affiche « Plus vieux que 20 ans » à l'écran. Il n'en est rien! Comme dit plus haut, seule la première condition valide remporte la mise. Le programme affichera « Plus vieux que 5 ans ».

Voici la bonne méthode à utiliser :

age = 21

if âge >30
puts "Plus vieux que 30 ans"
elsif âge >20
puts "Plus vieux que 20 ans"
elsif âge >10
puts "Plus vieux que 10 ans"
elsif âge >5
puts "Plus vieux que 5 ans"
end

II-C-6 - Quelques exercices

1. Triez ces caractères en utilisant l'ordre ASCII : 2, u, A, 4, w, f, R, y

2. Souvenez-vous, la table ASCII contient tous les caractères pouvant être générés par le clavier. Utilisez IRB pour déterminer où se trouve le caractère « ? » (point d'interrogation) ?

1. Avant 0 ?

2. Après 9 mais avant A ?

3. Après Z mais avant a ?

4. Après z ?

3. En se basant sur la question précédente, écrivez un programme Ruby qui accepte un caractère quelconque au clavier. Il devra afficher un des messages suivants :

1. x se situe avant 0

2. x se situe entre 9 et A

3. x se situe entre Z et a

4. x se situe après z

II-D - Les boucles while

Maintenant que vous êtes familiers avec les conditions dans Ruby, il est temps de voir un autre type de boucle: la boucle while («tant que»).

Cette boucle est un peu plus intéressante que celle que vous venez de voir. Elle se base sur une condition :

while condition
...
end

Ou condition n'est rien d'autre qu'une expression conditionnelle, comme celles que nous venons d'appréhender précédemment.

II-D-1 - Compteur

Voici un exemple tout simple, illustrant l'utilisation d'une boucle while :

Analysons le programme.

1. Initialisation de compteur à 0.

2. Comme compteur est bien inférieur à 10, nous rentrons dans la boucle.

3. Dans la boucle, nous affichons un message, et nous incrémentons compteur de 1. Maintenant, compteur vaut 1.

4. Comme compteur est toujours inférieur à 10, nous sautons à nouveau dans la boucle.

5. …

Le scénario se répétera jusqu'à ce que compteur soit égal à 10. Voici la sortie du programme :

En d'autres mots, une boucle while répétera la boucle tant que la condition sera évaluée comme étant vraie.

II-D-2 - Puissance de 2

Certaines choses sont faciles à réaliser avec une boucle while, mais moins évidentes avec n.times.

Supposez que nous voulons calculer la plus grande puissance de 2 plus petite que 10000. C'est très facile à programmer avec une boucle while :

Voici ce que ça donne :

Imaginez combien ça aurait été difficile d'arriver au même résultat, en utilisant n.times.

II-D-3 - Quelques exercices

- Reimplémentez le dernier programme, pour qu'il demande à l'utilisateur une valeur maximale (à la place de 10000). Le programme devra se comporter de cette façon :

- Démarrez le même programme, en y entrant 1e10 comme entrée. Que se passe-t-il ?

Si vous avez utilisé la méthode String#to_i, il y a des chances pour que 1e10 soit converti par le nombre 1. Essayons de comprendre pourquoi dans IRB :

Comme vous pouvez le remarquer, 1e10 est en Float (nombre flottant). Donc, vous devez utiliser la méthode String#to_s.

II-E - Écrire de bons programmes

II-E-1 - Commentaires

Un commentaireest une zone de texte dont chaque ligne est préfixée par le symbole #. Ces lignes seront ignorées par Ruby, donc vous pouvez les utiliser pour écrire des commentaires pour votre usage personnel. Par exemple :

# Calcule la plus grande puissance de 2
# inférieure à 10000.

nombre = 1
while nombre < 10_000
nombre *= 2
end

Les commentaires sont un peu comme des notes personnelles. Simplement pour vous aider à vous souvenir de ce que vous avez écrit.

II-E-1-a - Comment utiliser des commentaires

- Écrivez des commentaires pour chaque étape de votre programme. Essayez de les rendre clairs et précis.

- Expliquez toujours ce votre code fait, pas comment il le fait. Si le comment n'est pas évident en lisant le commentaire, alors vous devez réorganiser votre code.

- Dans le doute, il vaut mieux écrire plus de commentaires, que pas assez.

II-E-2 - Indentation

L'indentation se résume à une mise en page réalisée à l'aide de tabulations ou d'espaces, afin de rendre un code source plus lisible.

Indentez toujours votre code. Il s'agit d'un des principes les plus fondamentaux, que vous devez assimiler si vous désirez produire de bons programmes.

Bien

# Additionne des nombres impairs
nombre, somme = 1, 0
while nombre < 100
# % donne le reste
if nombre % 2 == 1
somme += nombre
end
puts somme
end

Pas bien

# Additionne des nombres impairs
nombre, somme = 1, 0
while nombre < 100
# % donne le reste
if nombre % 2 == 1
somme += nombre
end
puts somme
end

Il existe plusieurs standards en matière d'indentation. En général, les programmeurs utilisent soit une tabulation, soit 2, 4 ou 8 espaces. Choisissez le style qui vous convient le mieux !

III - Structures de données

La plus grande force de Ruby réside dans ses structures de données. Ce chapitre vous enseignera ces concepts pour écrire des programmes bien plus intéressants. Vous allez apprendre comment exprimer facilement des informations complexes, comme un carnet d'adresses complet. Ce chapitre est indispensable si vous désirez créer des programmes plus ou moins complexes.

III-A - Tableaux : introduction

Vous vous êtes déjà familiarisé avec deux classes Ruby, Integer et String. La classe Array représente une collection d'objets. Voici un exemple :

La méthode class nous indique que la variable nombres est un Array (plus précisément, un objet provenant de la classe Array). Vous pouvez accéder aux éléments du tableau de cette façon :

Vous pouvez rajouter des éléments dans le tableau tout simplement en tapant :

Remarquez que les éléments d'un tableau sont enregistrés séquentiellement. Un tableau peut contenir autant d'objets que vous le désirez. Les objets contenus dans le tableau peuvent être manipulés comme nous venons de voir :

Un tableau commence à compter à 0, pas 1 ! Ainsi, nombres[1] nous donnera le deuxième élément du vecteur, et non pas le premier.

Quels types de choses pouvons-nous insérer dans un tableau ? N'importe quel objet, en fait. Par exemple, des entiers et des chaines de caractères :

Et pourquoi pas un autre tableau ?

Vous comprenez pourquoi les tableaux sont vraiment intéressants ?

III-B - Que peuvent faire les tableaux?

Nous allons découvrir dans cette section quelques méthodes utiles concernant les tableaux en Ruby.

III-B-1 - Array#sort

Vous pouvez trier les éléments d'un tableau en utilisant la méthode Array#sort :

III-B-2 - Array#reverse

Inverser les éléments d'un tableau se fait tout aussi facilement :

III-B-3 - Array#length

La méthode Array#length vous donne le nombre d'éléments d'un tableau :

III-B-4 - Opérations arithmétiques sur des tableaux

Les méthodes Array#+, Array#- et Array#* se comportent logiquement :

Il n'existe évidemment pas de méthode Array#/, vu qu'il est impossible de diviser un tableau.

Souvenez-vous des raccourcis +=, -= et *=. Ils fonctionnent également avec des tableaux.

III-B-5 - Afficher le contenu d'un tableau

Finalement, nous pouvons bien sûr afficher le contenu d'un tableau à l'écran, en utilisant l'instruction puts :

Souvenez-vous, nil signifie que puts ne renvoie rien.

Notez également qu'il est possible de convertir un tableau en une chaine de caractères, en invoquant la méthode Array#to_s :

III-B-6 - Quelques exercices

Selon vous, que fera ce bout de code ?

>>adresses = [
[ 17, "Boulevard de la Sauvenière" ],
[ 2, "Place de la République Française"],
[ 19, "Rue de la Renaissance"]
]
>>adresses.sort

Et ceci?

>>adresses = [
[ 20, "Rue de la Renaissance" ],
[ 20, "Place de la République Française"]
]
>>adresses.sort

N'oubliez pas votre ami IRB !

III-C - Itérateurs

Cette section illustre une des fonctionnalités les plus intéressantes de Ruby : les itérateurs.

Un itérateur est une méthode un peu particulière. Il s'agit d'une méthode vous permettant d'accéder un par un à des éléments.

Les tableaux font partie des objets capables de supporter des itérateurs. Voici un exemple tout simple, utilisant la méthode Array#each :

amis = [ "Benjamin", "David", "Stéfanie", "Laura" ]
amis.each do |ami|
puts "J'ai un ami qui s'appelle " + ami
end

Ce qui produira à l'écran :

J'ai un ami qui s'appelle Benjamin
J'ai un ami qui s'appelle David
J'ai un ami qui s'appelle Stéfanie
J'ai un ami qui s'appelle Laura

Le lecteur attentif aura peut-être remarqué que cette méthode d'itération ressemble fort à la toute première boucle illustrée dans ce tutoriel (celle avec la forme n.times do … end). Il s'agit effectivement d'un itérateur! Cette méthode vous permet d'itérer sur la séquence d'entiers compris entre 0 et n-1. Regardez :

4.times do |nombre|
puts nombre
end

Ce qui produira à l'écran :

0
1
2
3

Vous remarquerez que l'itérateur times commence à compter à 0. Un peu comme les indices des tableaux (0 représentant le premier élément). Donc, le code suivant :

amis = [ "Benjamin", "David", "Stéfanie", "Laura" ]
amis.each do |ami|
puts "J'ai un ami qui s'appelle " + ami
end

Peut également être réécrit comme ceci :

# 'i' est une notation standard pour représenter un indice.
4.times do |i|
puts "J'ai un ami qui s'appelle " + amis[i]
end

Maintenant, voici quelque chose d'amusant. Vous souvenez-vous de la méthode Array#length? Nous pouvons améliorer notre code en l'utilisant :

amis.length.times do |i|    # 'i' pour indice
puts "J'ai un ami qui s'appelle " + amis[i]
end

Essayons maintenant d'afficher uniquement les éléments du tableau dont l'indice est pair. Le moyen le plus facile de déterminer si un entier est pair est de vérifier si le reste de sa division avec 2 est bien égal à 0. Souvenez-vous, l'opérateur Integer#% donne le reste d'une division. Voici le code :

amis.length.times do |i|    # 'i' pour indice
# Nous n'affichons que les indices pairs
if i % 2 == 0
puts "J'ai un ami qui s'appelle " + amis[i]
end
end

Ce qui produira :

J'ai un ami qui s'appelle Benjamin
J'ai un ami qui s'appelle Stéfanie

Comment faire pour afficher les amis dans l'ordre alphabétique? Rien de plus simple, il suffit d'utiliser la méthode Array#sort :

amis.sort.each do |ami|
puts "J'ai un ami qui s'appelle " + ami
end

Ce qui produira :

J'ai un ami qui s'appelle Benjamin
J'ai un ami qui s'appelle David
J'ai un ami qui s'appelle Laura
J'ai un ami qui s'appelle Stéfanie

III-C-1 - Quelques exercices

- Affichez le contenu du tableau amis dans l'ordre inverse alphabétique.

- Vous souvenez-vous de l'exercice du chapitre 2, où vous deviez afficher à l'écran les paroles de la chanson «Un kilomètre à pied, ça use…» ? Réimplémentez votre solution, en utilisant cette fois-ci un itérateur!

- Considérez le tableau suivant :

noms = [ "laurent", "david", "stéfanie", "laura" ]

Écrivez à l'écran la liste de ces noms, avec une majuscule comme première lettre. La méthode à utiliser est String#capitalize.

III-D - Hachages

Nous allons étudier dans cette section la classe Hash.

Les hachages ne sont rien d'autre que des tableaux spécialisés. Au lieu de n'accepter que des indices représentés par des nombres entiers (comme dans mon_tableau[3]), les hachages acceptent n'importe quel objet comme «index». Par exemple, vous pouvez écrire mon_hachage[« une_chaine_de_caractères »].

Supposons que nous voulons enregistrer des informations concernant un ami. Nous pourrions utiliser un tableau, comme ceci :

ami = [ "Jean-Paul", "Goret", "Rue de l'église, 26", "Houtsiplou", "Liège" ]

Évidemment, ceci fonctionnera. Mais nous allons devoir nous souvenir que ami[0] pointe vers le prénom, ami[1] vers le nom de famille, et ainsi de suite. Ceci peut se révéler fort compliqué par la suite.

C'est exactement le type de problème qu'un hachage peut résoudre. Voici la définition d'un hachage en Ruby :

ami = {
"prénom"          =>"Jean-Paul",
"nom de famille"  =>"Goret",
"adresse"         =>"Rue de l'église, 26",
"ville"           =>"Houtsiplou",
"province"        =>"Liège"
}

Remarquez bien qu'il faut utiliser des accolades pour créer un hachage. Les tableaux, quant à eux, se définissent par l'intermédiaire de crochets.

Comme les tableaux, il est tout à fait possible de rajouter par la suite des champs à un hachage :

ami["pays"] = "Belgique"

III-D-1 - Quelques définitions : clef et valeur

Comme les hachages n'utilisent pas que des indices numériques, on utilise le mot clef (en anglais : «key») pour les identifier. Un objet représenté à travers une clef est appelé valeur (en anglais : «value»).

Donc, dans l'exemple précédent, «prénom», «nom de famille» et «pays» sont des clefs. Leurs valeurs respectives sont «Jean-Paul», «Goret» et «Belgique».

III-D-2 - Hachages et itérateurs

Évidemment, les hachages possèdent des itérateurs. En voici quelques-uns

III-D-2-a - Hash#each

À l'instar des tableaux, les hachages ont également une méthode each. Cependant, elle nous donne à la fois la clef et la valeur de l'entrée. En voici un exemple :

amis.each do |clef, valeur|
puts clef + " =>" + valeur
end

Et voici son résultat à l'écran :

ville =>Houtsiplou
nom de famille =>Goret
pays =>Belgique
adresse =>Rue de l'église, 26
province =>Liège
prénom =>Jean-Paul

Ceci illustre un des plus gros défauts des hachages. Les données contenues dans un hachage ne sont pas classées dans un ordre particulier. Comment Ruby pourrait-il savoir que « prénom » doit venir avant « adresse » ?

Le meilleur moyen de résoudre ce type de problème est de créer sa propre classe. Ruby est excellent pour ça. Mais avant d'étudier cette technique, il y a encore quelques notions que nous devons voir. Le prochain chapitre vous expliquera comment créer vos propres classes et méthodes.

Hash#each_pair est un synonyme de Hash#each

III-D-2-b - Hash#each_key

Le nom de cette méthode nous dit tout. Elle permet de parcourir toutes les clefs d'un hachage, un peu de la même façon que Hash#each :

>>ami.each_key do |clef|
?>   puts clef
>>end
ville
nom de famille
pays
adresse
province
prénom

III-D-2-c - Hash#each_value

Cette méthode parcourt toutes les valeurs d'un hachage :

>>ami.each_value do |valeur|
?>   puts valeur
>>end
Houtsiplou
Goret
Belgique
Rue de l'église, 26
Liège
Jean-Paul

III-E - Exemple pratique : un carnet d'adresses

Dans cette section, nous allons construire un petit carnet d'adresses, contenant des informations sur trois amis : Nicolas, François et Marina.

La structure d'un carnet d'adresses est relativement complexe. Elle doit contenir plusieurs personnes, chacun ayant un nom, une adresse, et ainsi de suite.

Pour implémenter ce carnet d'adresses, notre stratégie sera de découper le problème en plusieurs petits problèmes.

III-E-1 - Première étape : analyse

Il nous faut commencer par déterminer le type d'informations que notre carnet d'adresses devrait référencer :

1. Le carnet d'adresses doit contenir une liste de personnes. Nous devrions être capables de trier ces personnes par ordre alphabétique ;

2. Chaque personne possède évidemment un prénom, un nom de famille, un numéro de téléphone ainsi qu'une adresse ;

3. Chaque adresse est définie par le nom d'une rue, d'un code postal ainsi que d'une ville et le pays dans lequel elle se situe.

Nous allons commencer par définir une structure représentant une adresse, et nous finirons par assembler le carnet tout entier.

III-E-2 - Deuxième étape : adresses

Nous avons ici deux possibilités pour implémenter la structure d'une adresse :

1. Tableau : nous pourrions ajouter successivement l'adresse, le code postal, la ville et le pays dans un tableau. Ça fonctionnerait ;

2. Hachage : évidemment, c'est plus facile de se souvenir d'une expression comme adresse[« ville »] plutôt que adresse[2].

Nous allons évidemment choisir d'utiliser un hachage. Voici la représentation de nos adresses :

# Adresse de Nicolas
adresse_de_nicolas = {
"rue"         =>"Rue du port, 32",
"code postal" =>"56000",
"ville"       =>"Vannes",
"pays"        =>"France"
}

# Adresse de François
adresse_de_francois = {
"rue"         =>"Avenue de la tranchée, 14",
"code postal" =>"1000",
"ville"       =>"Bruxelles",
"pays"        =>"Belgique"
}

# Adresse de Marina
adresse_de_marina = {
"rue"         =>"Strada di l'amore, 61",
"code postal" =>"50100",
"ville"       =>"Firenze",
"pays"        =>"Italia"
}

III-E-3 - Troisième étape : personnes

Chaque personne est identifiée par un prénom, un nom de famille, un numéro de téléphone ainsi qu'une adresse. Comme pour la structure précédente, un hachage convient parfaitement. Voici comment nous pouvons représenter nos amis avec Ruby :

# Nicolas
nicolas = {
"prénom"         =>"Nicolas",
"nom de famille" =>"Rocher",
"téléphone"      =>"(+33) 02 93 45 49 19",
"adresse"        =>adresse_de_nicolas
}

# François
francois = {
"prénom"         =>"François",
"nom de famille" =>"Willemart",
"téléphone"      =>"(+32) 02 679 24 81",
"adresse"        =>adresse_de_francois
}

# Marina
marina = {
"prénom"         =>"Marina",
"nom de famille" =>"Nantini",
"téléphone"      =>"(+39) 055 681 32 11",
"adresse"        =>adresse_de_marina
}

III-E-4 - Quatrième étape : carnet d'adresses

Maintenant que nous avons défini toutes les autres structures, il ne nous reste plus qu'à créer le carnet d'adresses. Comme nous désirons pouvoir par la suite trier les éléments, nous devons donc utiliser un tableau pour réunir nos trois amis :

carnet = [ nicolas, francois, marina ]

Et voilà ! Nous venons d'implémenter complètement la structure de notre carnet d'adresses. Dans la section suivante, nous allons apprendre comment trier les éléments du carnet, ainsi que d'autres petites choses amusantes.

Sauvegardez les structures dans un fichier. Nous allons les réutiliser dans les sections qui vont suivre.

III-E-5 - Quelques exercices

1. Ajoutez un de vos amis dans le carnet d'adresses.

2. Modifiez la structure du carnet d'adresses afin qu'il puisse contenir des adresses e-mail.

III-F - Afficher le contenu du carnet

Nous allons apprendre dans cette section comment afficher à l'écran le contenu de notre carnet d'adresses.

III-F-1 - Afficher des structures de données complexes

Nous pouvons toujours taper puts carnet, mais le résultat n'est pas très lisible pour un être humain (essayez dans IRB par vous-même). Nous voudrions mieux définir notre propre moyen pour afficher son contenu.

Comme carnet est un tableau, nous pouvons utiliser l'itérateur Array#each. Commençons par simplement afficher le prénom de nos amis :

carnet.each do |personne|
puts personne["prénom"]
end

Ce qui affichera :

Nicolas
François
Marina

III-F-2 - Noms complets

La prochaine étape est d'afficher les noms complets :

carnet.each do |personne|
prenom = personne["prénom"]
nom    = personne["nom de famille"]
puts prenom + " " + nom
end

Nicolas Rocher
François Willemart
Marina Nantini

III-F-3 - Numéros de téléphone

Rajouter le numéro de téléphone n'est pas plus difficile :

carnet.each do |personne|
prenom = personne["prénom"]
nom    = personne["nom de famille"]
tel    = personne["téléphone"]
puts prenom + " " + nom
puts "    " + tel
end

Le résultat :

Nicolas Rocher
(+33) 02 93 45 49 19
François Willemart
(+32) 02 679 24 81
Marina Nantini
(+39) 055 681 32 11

III-F-4 - Adresses

Finalement, il ne nous reste plus qu'à afficher les adresses. Voici le code qui devrait effectuer cette requête, en prenant soin d'espacer chaque personne par une nouvelle ligne :

carnet.each do |personne|
# Nom et téléphone
prenom = personne["prénom"]
nom    = personne["nom de famille"]
tel    = personne["téléphone"]

puts prenom + " " + nom
puts "    " + tel

# Adresse
rue    = personne["adresse"]["rue"]
cp     = personne["adresse"]["code postal"]
ville  = personne["adresse"]["ville"]
pays   = personne["adresse"]["pays"]

puts "    " + rue
puts "    " + cp + ", " + ville
puts "    " + pays

# Une ligne vide pour séparer les entrées
puts ""
end

Ce qui produira :

Nicolas Rocher
(+33) 02 93 45 49 19
Rue du port, 32
56000, Vannes
France

François Willemart
(+32) 02 679 24 81
Avenue de la tranchée, 14
1000, Bruxelles
Belgique

Marina Nantini
(+39) 055 681 32 11
Strada di l'amore, 61
50100, Firenze
Italia

Et voilà le travail !

III-G - Trier les éléments du carnet

Souvenez-vous, nous avons choisi d'utiliser un tableau pour contenir les différentes personnes de notre carnet d'adresses, parce que nous avions décidé au début qu'il devrait être possible de trier ces personnes, selon des critères quelconques.

Il est maintenant temps de trier les éléments de notre carnet.

III-G-1 - Retour sur Array#sort

Nous avons déjà utilisé Array#sort pour trier des tableaux contenant des nombres entiers, et des chaines de caractères. Mais comment pouvons-nous trier des tableaux contenant des structures de données plus complexes (comme nos hachages) ?

Lorsque le comportement par défaut n'est pas ce que vous désirez, Array#sort vous permet de spécifier comment trier le tableau. Commençons avec un exemple beaucoup plus facile que notre carnet d'adresses actuel :

amis = [
[ "Nicolas",  "Rocher"    ],
[ "François", "Willemart" ],
[ "Marina",   "Nantini"   ]
]

Nous savons que par défaut la méthode Array#sort appelée sur l'objet amis effectuera le tri en fonction du premier élément de chaque sous-tableau - dans ce cas, les prénoms. Et si nous voulions trier ces éléments par leur nom de famille ?

À l'instar des itérateurs, la méthode Array#sort peut accepter un bloc de code. Comme ceci :

amis.sort do |a, b|
# ...
end

La méthode Array#sort attend trois comportements de ce bloc de code :

- Retourner la valeur -1 si la variable a est jugée plus petite que la variable b ;

- Retourner la valeur 0 si la variable a est jugée égale à la variable b ;

- Retourner la valeur 1 si la variable a est jugée plus grande que la variable b.

Grâce a cette information, la méthode Array#sort peut savoir exactement comment il faut trier le tableau.

III-G-2 - Retourner des valeurs

Comment faire pour retourner une valeur? La valeur de retour est tout simplement la valeur de la dernière expression interprétée. Jetons un petit coup d'œil dans IRB :

Les lignes =>sont les valeurs de retour des expressions.

III-G-3 - L'opérateur <=>

Trier étant une opération fort courante, il existe donc un opérateur spécialement dédié à ça. L'opérateur <=>retourne pour nous les fameuses valeurs -1, 0 et 1. Essayez ceci dans IRB :

III-G-4 - Trier sur le nom de famille

Retournons à notre petit tableau :

amis = [
[ "Nicolas",  "Rocher"    ],
[ "François", "Willemart" ],
[ "Marina",   "Nantini"   ]
]

Et à notre instruction de triage :

amis.sort do |a, b|
# ...
end

a et b sont des éléments du tableau amis. Le comportement par défaut de Array#sort est équivalent à :

amis.sort do |a, b|
a[0] <=>b[0]   # Tri sur le premier élément
end

Mais comme nous voulons trier sur le second élément (c'est-à-dire le nom de famille), nous devons écrire :

amis = amis.sort do |a, b|
a[1] <=>b[1]   # Tri sur le second élément
end
# Maintenant, les éléments du tableau 'amis' sont triés sur le nom de famille.

III-G-5 - Trier le carnet d'adresses

Maintenant que nous connaissons Array#sort un peu mieux, nous sommes prêts à personnaliser le tri du carnet d'adresses. Nous avons :

carnet.sort do |personne_a, personne_b|
# ...
end

personne_a et personne_b sont tous deux des objets provenant du même hachage. Nous pouvons donc, par exemple, les trier par ordre alphabétique sur leur prénom, en écrivant tout simplement ceci :

carnet.sort do |personne_a, personne_b|
personne_a["prénom"] <=>personne_b["prénom"]
end

Essayez, ça fonctionne !

III-G-6 - Quelques exercices

Écrivez le code Ruby permettant de trier chaque élément du tableau carnet en fonction du nom complet (c'est-à-dire, le prénom et le nom de famille).

III-H - Écrire de bons programmes

III-H-1 - Tableau ou hachage ?

Quand faut-il utiliser un tableau ? Quand faut-il utiliser un hachage ?

La structure du carnet d'adresses est un bon exemple pour répondre à ces deux questions.

- Si vos données doivent être triées, il vous faut un tableau.

- Si vos données représentent une collection de données différentes, sans aucune corrélation entre elles (par exemple, un prénom, un nom et un numéro de téléphone), il vous faut un hachage.

- Par contre, si les éléments appartiennent à la même catégorie de données (par exemple, une liste de noms), il vous faut un tableau.

Dans l'exemple du carnet d'adresses, les structures pour les adresses et les personnes contiennent des données différentes. Donc, nous les avons représentés par des hachages.

Mais le carnet lui-même contient des données du même type (des personnes), et nous voulions également être capables de trier ses éléments. Donc, représenter le carnet par un tableau était le meilleur choix.

III-H-2 - Noms de variables

Comme vous êtes maintenant capable d'écrire des structures de données complexes, il devient plus important que vous choisissiez de bons noms pour vos variables.

III-H-2-a - Tableaux

Un tableau devrait toujours représenter une collection d'éléments du même type. Comme une groupe de voitures, une liste de noms, etc.. Vous pouvez représenter ceci en utiliser en mettant au pluriel nom de votre tableau. Par exemple :

- si chaque élément du tableau est une voiture, le tableau devrait s'appeler voitures ;

- si chaque élément du tableau est le nom d'une personne, le tableau devrait s'appeler noms.

De cette façon, le nom de la variable vous rappelle qu'il s'agit d'un tableau, et en même temps, elle se lit comme du français.

III-H-2-b - Hachages

Lorsque vous travaillerez avec des hachages, il est important que vous choisissiez de bons noms pour vos clefs de hachage. Un bon nom doit être à la fois clair, descriptif et facile à se souvenir.

- Bien : « deuxième prénom »

- Pas bien : « 2eprenom »

N'oubliez pas que Ruby accepte des espaces et des accents comme noms pour vos clefs de hachage. Vous n'avez donc aucune excuse d'utiliser « 2eprenom » pour une clef.

III-H-3 - Commentaires

N'oubliez surtout pas de rajouter des commentaires clairs dans vos programmes. Souvenez-vous, on écrit le code une fois, et on le lit une infinité de fois.

III-H-4 - Sous-structures

En général, il est recommandé d'éviter de déclarer des sous-structures. Néanmoins, si vous utilisez une indentation propre et des noms significatifs pour vos variables, ça peut être bon.

Jetez un coup d'œil à ces exemples :

III-H-4-a - Bien

# Adresse de Nicolas
adresse_de_nicolas = {
"rue"         =>"Rue du port, 32",
"code postal" =>"56000",
"ville"       =>"Vannes",
"pays"        =>"France"
}

# Nicolas
nicolas = {
"prénom"         =>"Nicolas",
"nom de famille" =>"Rocher",
"téléphone"      =>"(+33) 02 93 45 49 19",
"adresse"        =>adresse_de_nicolas
}

III-H-4-b - Peut mieux faire

# Nicolas
nicolas = {
"prénom"          =>"Nicolas",
"nom de famille"  =>"Rocher",
"téléphone"       =>"(+33) 02 93 45 49 19",
"adresse"         =>{
"rue"         =>"Rue du port, 32",
"code postal" =>"56000",
"ville"       =>"Vannes",
"pays"        =>"France"
}
}

III-H-4-c - Pas bien

# Nicolas
nicolas = {
"prénom" =>"Nicolas",
"nom de famille" =>"Rocher",
"téléphone" =>"(+33) 02 93 45 49 19",
"adresse" =>{
"rue" =>"Rue du port, 32",
"code postal" =>"56000",
"ville" =>"Vannes",
"pays" =>"France"
}}

IV - Classes et méthodes

À propos de ce chapitre

Vous venez d'apprendre comment créer vos propres structures de données. Dans ce dernier chapitre, nous passerons à la vitesse suivante, en transformant ces structures en classes (Adresse, Personne et Carnet), et en leur ajoutant des méthodes.

Ce seront de véritables classes Ruby, au même titre que Integer, String, Array ou Hash.

Créer ses propres classes procure quelques avantages intéressants :

- Réutilisation du code : vous souvenez-vous du nombre de lignes qu'il a fallu écrire pour afficher le contenu du carnet d'adresses? N'aurait-il pas été plus joli d'écrire ce code quelque part, et d'ensuite appeler puts carnet chaque fois que nous désirions l'afficher ? C'est exactement ce que nous allons apprendre dans ce chapitre ;

- Abstraction des données : vous utilisez une classe en appelant ses méthodes. Le contenu de la classe en elle-même (l'implémentation des méthodes) vous est caché. Savez-vous comment la classe String est fabriquée? Probablement pas, mais vous êtes néanmoins capable de l'utiliser.

IV-A - Fonctions

IV-A-1 - Qu'est-ce qu'une fonction ?

Une fonction est une méthode qui n'est pas associée à un objet en particulier. Vous avez déjà utilisé une fonction auparavant : puts. Remarquez bien la syntaxe :

puts "Salut!"     # à la place de :  un_objet.puts "Salut!"

IV-A-2 - « Bonjour monde » avec une fonction

Voici une fonction toute simple :

def bonjour
puts "Bonjour monde!"
end

Maintenant, nous avons défini la fonction bonjour. Le code qu'elle contient sera exécuté à chaque appel de la fonction. Un exemple :

def bonjour
puts "Bonjour monde!"
end

bonjour
bonjour

Qui produira :

Bonjour monde!
Bonjour monde!

Comme vous pouvez dès à présent le constater, les fonctions peuvent servir à réutiliser du code facilement.

IV-A-3 - Paramètres de fonction

Vous savez déjà qu'il est possible de passer des paramètres aux méthodes et fonctions. Mais vous ne savez pas encore comment faire! Voici la fonction bonjour légèrement améliorée :

def bonjour(nom)
puts "Bonjour " + nom + ", comment vas-tu"
end

bonjour("Laurent")
bonjour "Stéfanie"

Ce qui produira :

Bonjour Laurent, comment vas-tu?
Bonjour Stéfanie, comment vas-tu ?

IV-A-4 - Afficher une adresse

Écrivons maintenant une fonction un peu plus utile. Souvenez-vous des structures d'adresses du chapitre précédent :

# Adresse de Nicolas
adresse_de_nicolas = {
"rue"         =>"Rue du port, 32",
"code postal" =>"56000",
"ville"       =>"Vannes",
"pays"        =>"France"
}

# Adresse de François
adresse_de_francois = {
"rue"         =>"Avenue de la tranchée, 14",
"code postal" =>"1000",
"ville"       =>"Bruxelles",
"pays"        =>"Belgique"
}

Voici le code d'une fonction qui permet de les afficher à l'écran :

def affiche_adresse(adresse)
code_postal = adresse["code postal"]
ville       = adresse["ville"]

puts "    " + adresse["rue"]
puts "    " + code_postal + ", " + ville
puts "    " + adresse["pays"]
end

Maintenant, nous pouvons facilement afficher des adresses :

puts "Nicolas:"
affiche_adresse(adresse_de_nicolas)

puts "François:"
affiche_adresse(adresse_de_nicolas)

Ce qui produira à l'écran :

Nicolas:
Rue du port, 32
56000, Vannes
France
François:
Rue du port, 32
56000, Vannes
France

IV-B - Classes et méthodes

Nous sommes maintenant prêts pour créer notre propre classe Adresse. Commençons simplement par une classe qui ne contient que le champ « rue ». Voici un exemple :

1
class Adresse
2
def initialize(rue)
3
@rue = rue
end
end

Explication

- 1 Le mot clef class définit une classe. On associe une méthode à une classe simplement en définissant la méthode à l'intérieur de la classe.

- 2 La méthode initialize permet de construire la classe. Il s'agit en fait du constructeur de la classe. Chaque classe doit contenir une méthode initialize!

- 3 @rue est une variable de classe. Ça ressemble un peu à une clef de hachage. Le symbole @ permet de distinguer les variables d'un objet. Chaque fois que vous allez instancier un objet de la classe Adresse, cet objet contiendra une variable @rue.

Utilisons maintenant cette classe pour créer un objet :

adresse = Adresse.new("Rue de la Renaissance, 49")

Et voilà le travail. adresse est dès à présent un objet provenant de la classe Adresse.

IV-B-1 - Lire les données d'un objet

Comment s'y prendre pour obtenir la rue de notre objet adresse? Nous pouvons par exemple écrire une méthode qui renvoie les données :

class Adresse
def initialize(rue)
@rue = rue
end

# Renvoie simplement @rue
def rue
@rue
end
end

Maintenant, la méthode Adresse#rue vous permet d'obtenir la rue d'une adresse. Dans IRB :

>>adresse.rue
=>"Rue de la Renaissance, 49"

Une propriété d'un objet, visible de l'extérieur, est appelée attribut. Dans ce cas, rue est un attribut. Plus spécialement, un attribut en lecture, car il permet de lire la valeur d'une des propriétés de la classe.

Comme ce genre d'attribut se retrouve assez souvent, Ruby nous offre un raccourci : le mot clef attr_reader :

class Adresse
attr_reader :rue
def initialize(rue)
@rue = rue
end
end

IV-B-2 - Modifier les données d'un objet

Évidemment, il est également possible de modifier une propriété d'un objet. Par exemple, en rajoutant une méthode dans la classe :

class Adresse
attr_reader :rue
def initialize(rue)
@rue = rue
end
def rue=(une_rue)
@rue = une_rue
end
end

Et voici comment utiliser cette nouvelle méthode :

adresse.rue = "Une autre adresse"

Remarquez que Ruby accepte des espaces entre rue et = (dans l'affectation). Par contre, il ne faut pas mettre d'espaces dans la définition de la méthode.

Maintenant que nous savons comment modifier les données de notre classe, nous n'avons plus besoin d'initialiser la rue dans le constructeur. Nous pouvons donc simplifier la méthode initialize :

class Adresse
attr_reader :rue
def initialize
@rue = ""
end
def rue=(une_rue)
@rue = une_rue
end
end

adresse = Adresse.new
adresse.rue = "Rue de la Renaissance, 49"

Cette petite modification rendra le code plus simple par la suite, lorsque nous rajouterons les autres données (le code postal, la ville, le pays).

Maintenant, rue est également un attribut en écriture, car via la méthode Adresse#rue=, il nous est possible de modifier sa valeur. Comme pour la lecture, il s'agit d'une opération courante, et Ruby nous permet d'utiliser le mot clef attr_writer :

class Adresse
attr_reader :rue
attr_writer :rue
def initialize
@rue = ""
end
end

IV-B-3 - Accéder à des données

Fort souvent, vous aurez besoin d'attributs qui fonctionneront à la fois en lecture et en écriture. Comme vous vous en doutez peut-être, Ruby possède un mot clef qui regroupe ces deux états à la fois : attr_accessor :

class Adresse
attr_accessor :rue
def initialize
@rue = ""
end
end

Il est maintenant temps de définir entièrement la structure adresse de notre carnet sous la forme d'une classe. Le code ci-dessous ne devrait pas vous poser de problème :

class Adresse
attr_accessor :rue, :code_postal, :ville, :pays
def initialize
@rue = @code_postal = @ville = @pays = ""
end
end

Notez que attr_accessor accepte plusieurs arguments.

IV-C - Plus de classes

IV-C-1 - Une classe Personne

Créons maintenant une classe Personne. Une personne doit avoir un prénom, un nom de famille, une adresse e-mail, un numéro de téléphone, et une adresse physique (celle que nous venons d'implémenter à la section précédente) :

class Personne
attr_accessor :prenom, :nom, :email, :telephone, :adresse
def initialize
@prenom = @nom = @email = @telephone = ""
@adresse = Adresse.new
end
end

La seule chose qui pourrait vous surprendre dans cet exemple, c'est la ligne @adresse = Adresse.new. La variable @adresse n'est pas une chaine de caractères comme les autres variables, mais un objet issu de la classe Adresse.

Il est maintenant possible de créer une personne :

# Adresse:
adresse_de_nicolas             = Adresse.new
adresse_de_nicolas.rue         = "Rue du port, 32"
adresse_de_nicolas.code_postal = "56000"
adresse_de_nicolas.ville       = "Vannes"

# Personne:
nicolas         = Personne.new
nicolas.prenom  = "Nicolas"
nicolas.nom     = "Rocher"
nicolas.email   = "nicolas.rocher@free.fr"
nicolas.adresse = adresse_de_nicolas

Notez que nous n'avons ni affecté de pays, ni de numéro de téléphone à Nicolas. Comme par défaut, toutes les valeurs sont vides (c'est-à-dire « », nous pouvons créer des objets Adresse et Personne avec des informations incomplètes. Et nous pouvons toujours appeler nicolas.telephone sans aucun problème, Ruby nous renverra « » en retour. Essayez dans IRB !

Et si nous ajoutions une méthode dans la classe Personne, qui nous renverrait le nom complet de la personne représentée? Très facile :

class Personne
attr_accessor :prenom, :nom, :email, :telephone, :adresse
def initialize
@prenom = @nom = @email = @telephone = ""
@adresse = Adresse.new
end
def nom_complet
@prenom + " " + @nom
end
end

# ...

puts nicolas.nom_complet

Ce qui affichera « Nicolas Rocher ».

IV-C-2 - Afficher le contenu d'une classe

Ne serait-il pas magnifique si nous pouvions simplement taper puts adresse_de_nicolas? Ruby est capable d'exaucer ce rêve.

La fonction puts travaille de la façon suivante : elle essaye d'appeler la méthode to_s sur l'objet qu'on lui donne, et elle affiche le résultat sur l'écran. Vous vous souvenez de Integer#to_s et de ses amis?

La seule chose à faire, c'est de définir une méthode Adresse#to_s :

class Adresse
attr_accessor :rue, :code_postal, :ville, :pays
def initialize
@rue = @code_postal = @ville = @pays = ""
end
def to_s
"    " + @rue + "\n" + \
"    " + @code_postal + ", " + @ville + "\n" \
"    " + @pays
end
end

Le caractère \n représente une nouvelle ligne. Il s'agit un fait du caractère que votre clavier envoie lorsque vous pressez la touche Enter.

Nous pouvons maintenant taper ceci :

adresse             = Adresse.new
adresse.rue         = "Rue du port, 32"
adresse.code_postal = "56000"
adresse.ville       = "Vannes"
adresse.pays        =  "France"

puts adresse

Ce qui affichera :

Rue du port, 32
56000, Vannes
France

IV-C-3 - Quelques exercices

Écrivez une méthode Personne#to_s, qui devra afficher le nom complet de la personne, son adresse e-mail, son numéro de téléphone, et son adresse physique.

IV-D - Implémentation du carnet d'adresses

Maintenant que nous avons les classes Adresse et Personne, il ne nous reste plus qu'à créer la classe Carnet.

IV-D-1 - Première étape

Notre carnet d'adresses doit contenir un tableau, qui contient tous nos contacts. Nous n'utiliserons pas attr_accessor parce que nous ne voulons pas qu'on puisse accéder directement au tableau. Nous allons donc écrire nos propres méthodes pour travailler sur le tableau interne.

Voici à quoi peut ressembler le code de notre classe Carnet :

class Carnet
def initialize
# Initialise le tableau.  Meme chose que ``Array.new''.
@personnes = []
end
end

C'était plutôt facile. Rajoutons maintenant deux méthodes d'accès : Carnet#ajoute et Carnet#retire :

class Carnet
def initialize
@personnes = []
end
def ajoute(personne)
#1
@personnes.push(personne)
end
def retire(personne)
#2
@personnes.delete(personne)
end
end

Explication du code

- 1 : La méthode Array#push permet d'ajouter un objet dans un tableau. L'objet sera empilé sur les objets existants, un peu comme si vous rajoutiez une assiette sur une pile d'assiettes.

- 2 : La méthode Array#delete permet de supprimer un objet d'un tableau. Si le tableau contient plusieurs objets identiques, ils seront tous supprimés.

Pour mieux comprendre le fonctionnement de Array#delete, essayez ceci dans IRB :

>>a = [ 1, 3, 3, 3, 3, 5]
=>[1, 3, 3, 3, 3, 5]
>>a.delete(3)
=>3
>>a
=>[1, 5]

IV-D-2 - Tri automatique

Nous allons maintenant rajouter une super fonctionnalité dans notre Carnet : un tri automatique. Par exemple, imaginez le code suivant :

carnet = Carnet.new
carnet.ajoute nicolas
carnet.ajoute francois
carnet.ajoute marina

Le carnet classera automatiquement les personnes à chaque ajout. Cette fonctionnalité va rendre notre classe Carnet bien plus intéressante qu'un simple tableau.

IV-D-2-a - Comment trier un tableau?

Nous voulons que le tableau contenant les contacts soit trié par ordre alphabétique, en se basant sur le nom complet de la personne (prénom et nom de famille). Dans la section précédente, nous avons écrit ceci :

# ``carnet'' est un tableau ici
carnet.sort do |personne_a, personne_b|
personne_a["prénom"] <=>personne_b["prénom"]
end

Adaptons ce code pour notre classe Carnet :

@personnes.sort do |a, b|
a.prenom <=>b.prenom
end

Si vous avez effectué les exercices du chapitre précédent, vous devriez déjà savoir comment trier les personnes en se basant sur leur nom complet. Voici une façon de le faire :

@personnes.sort do |a, b|
if a.prenom == b.prenom
a.nom <=>b.nom
else
a.prenom <=>b.prenom
end
end

Si les prénoms sont les mêmes, nous comparons les noms de famille. Sinon, on compare les prénoms.

IV-D-2-b - Simplification

Le principe fondamental de la simplification est de diviser le problème en petites parties. Nous pouvons déplacer le bloc de code dans une méthode :

def par_nom(a, b)
if a.prenom == b.prenom
a.nom <=>b.nom
else
a.prenom <=>b.prenom
end
end

Maintenant, nous pouvons écrire :

@personnes.sort do |a, b| par_nom(a, b) end

Ce qui est beaucoup plus simple à lire.

Il est possible de définir avec Ruby des blocs de code en utilisant deux syntaxes différentes :

@personnes.sort do |a, b|
# ...
end

@personnes.sort { |a, b|
# ...
}

Ces deux notations veulent dire exactement la même chose. La différence est que do … end est plus lisible, et que { … } est plus court.

Nous pouvons écrire le tri de notre tableau de cette façon :

@personnes.sort { |a, b| par_nom(a, b) }

Vous pouvez littéralement lire « tri de personnes par le nom ». C'est du code très lisible. Voici une suggestion :

- utilisez la notation { … } quand il est possible de faire tenir l'expression sur une seule ligne ;

- sinon, utilisez do … end.

IV-D-2-c - Finalement

Il est maintenant temps de déplacer ce code dans notre classe Carnet, et d'implémenter notre tri automatique :

class Carnet
def initialize
@personnes = []
end
def ajoute(personne)
@personnes.push(personne)
@personnes = @personnes.sort { |a, b| par_nom(a, b) }
end
def retire(personne)
@personnes.delete(personne)
end
def par_nom(a, b)
if a.prenom == b.prenom
a.nom <=>b.nom
else
a.prenom <=>b.prenom
end
end
end

Maintenant, à chaque fois que vous ajouterez une personne dans le carnet, ce dernier sera trié automatiquement!

IV-E - Écrire des itérateurs

Dans cette section, nous allons ajouter deux itérateurs dans la classe Carnet : Carnet#chaque_personne et Carnet#chaque_adresse. Au résultat, nous pourrons écrire ceci :

carnet.chaque_personne do |personne|
# ...
end

carnet.chaque_adresse do |adresse|
# ...
end

IV-E-1 - Exécuter un bloc de code

Le mot clef yield permet d'appeler un bloc de code. Voici un exemple :

def deux_fois
yield
yield
end

deux_fois { puts "Vive Ruby!" }

Ce qui produira :

Vive Ruby!
Vive Ruby!

IV-E-2 - Passage de paramètres

Vous pouvez utiliser yield exactement comme n'importe quelle autre méthode. Pour passer des arguments à un bloc de code, passez-les simplement à yield. Prenez cet exemple :

def noms
yield("Nicolas")
yield("François")
yield("Marina")
end

noms do |nom|
puts "Salut " + nom + ", comment vas-tu?"
end

Ce qui affichera à l'écran :

Salut Nicolas, comment vas-tu?
Salut François, comment vas-tu?
Salut Marina, comment vas-tu?

Vous pouvez passer autant de paramètres que vous voulez au bloc de code. Par exemple :

def noms
yield("Nicolas", "Rocher")
end

noms do |prenom, nom|
puts prenom + " " + nom
end

Ce qui donnera :

Nicolas Rocher

IV-E-3 - Implémentation de Carnet#chaque_personne

Ce premier itérateur est le plus facile des deux à écrire : il suffit de parcourir chaque personne dans le tableau @personnes et d'appeler yield sur chaque élément :

class Carnet
# ...
def chaque_personne
@personnes.each { |p| yield(p) }
end
end

Et voilà !

IV-E-4 - Implémentation de Carnet#chaque_adresse

Cet itérateur est quasi aussi simple à écrire que le premier. Au lieu de passer chaque personne au bloc de code, nous allons passer l'adresse de cette personne :

class Carnet
# ...
def chaque_adresse
@personnes.each { |p| yield(p.adresse) }
end
end

IV-E-5 - Code complet de la classe Carnet

Juste pour tout mettre au clair, voici le code complet commenté de la classe Carnet. Il s'agit d'un morceau de code assez complexe, mais en découpant les tâches au fur et à mesure, il est beaucoup plus facile à maintenir :

class Carnet
#
#  Méthodes fondamentales:
#    initialize
#    ajoute
#    retire
#
def initialize
@personnes = []
end
def ajoute(personne)
@personnes.push(personne)
@personnes = @personnes.sort { |a, b| par_nom(a, b) }
end
def remove(personne)
@personnes.delete(personne)
end

#
#  Iterateurs:
#    chaque_personne
#    chaque_adresse
#
def chaque_personne
@personnes.each { |p| yield p }
end
def chaque_adresse
@personnes.each { |p| yield p.adresse }
end

#
#  Fonction de tri.
#
def par_nom(a, b)
if a.prenom == b.prenom
a.nom <=>b.nom
else
a.prenom <=>b.prenom
end
end
end

IV-F - Autres fonctionnalités

Avant de terminer, nous pouvons encore voir deux petites choses intéressantes concernant notre classe Carnet.

IV-F-1 - Méthodes publiques et privées

Prenez Carnet#par_nom. Cette méthode est différente des autres sur un point très important : elle est utilisée à l'intérieur même de la classe. C'est ce qu'on appelle une méthode interne, ou privée.

Lorsque vous programmerez une méthode comme celle-ci, il est possible de la déclarer comme étant privée. Une méthode privée ne peut être appelée que par l'objet lui-même, et jamais par l'utilisateur. Une méthode normale (comme toutes celles que nous avons écrites) est appelée méthode publique.

Vous pouvez déclarer des méthodes en utilisant les mots clefs public et private. Quand vous ajoutez le mot clef private, toutes les méthodes définies à partir de là seront privées, jusqu'à ce que vous rajoutiez le mot clef public.

Par exemple :

class UneClasse
# Par défaut, les méthodes sont publiques
def methode1
# ...
end

private   # Maintenant, les méthodes sont privées

def methode2
# ...
end
def methode3
# ...
end

public   # Sauf celle-ci, qui sera publique

def methode4
# ...
end
end

Donc, dans notre carnet d'adresses, nous devons rajouter le mot clef private juste avant la définition de Carnet#par_nom :

class Carnet
#
#  Méthodes fondamentales :
#    initialize
#    ajoute
#    retire
#
def initialize
@personnes = []
end

# ...

private   # Début des méthodes privées

#
#  Fonction de tri.
#
def par_nom(a, b)
if  a.prenom == b.prenom
a.nom <=>b.nom
else
a.prenom <=>b.prenom
end
end
end

IV-F-2 - Réutilisation du code avec require

Nous avons passé beaucoup de temps à écrire les trois classes pour notre carnet d'adresses. Nous ne voulons pas copier et coller éternellement le code chaque fois que nous allons l'utiliser dans un programme. Fort heureusement, nous n'avons pas besoin de le faire.

Copiez le code des trois classes dans un fichier, et sauvez-le sous le nom carnet.rb. Maintenant, créez un nouveau fichier (dans le même répertoire) et tapez :

require 'carnet'

# Nicolas

adresse = Adresse.new

adresse.rue         = "Rue du port, 32"
adresse.code_postal = "56000"
adresse.ville       = "Vannes"
adresse.pays        = "France"

puts "Nicolas:"
puts adresse

Sauvez-le sous le nom nicolas.rb par exemple. Maintenant, invoquez Ruby :

Vous l'aurez compris, le mot clef require permet de charger du code existant.

IV-G - Écrire de bons programmes

L'utilisation propice de fonctions, méthodes et classes permet de distinguer les programmeurs expérimentés. Vous trouverez dans cette dernière section quelques conseils qui vous aideront par la suite.

IV-G-1 - Fonctions et méthodes

Les fonctions et les méthodes doivent toujours se charger d'une seule chose :

- si vous ne pouvez pas résumer en une ligne ce que fait votre fonction, elle est probablement trop compliquée ;

- si le code de votre fonction ne tient pas sur un seul écran, elle est probablement trop longue ;

- des études ont démontré qu'un être humain se souvient au maximum de 7 choses à la fois. Si votre fonction comporte plus de 6 variables, elle est probablement trop compliquée, et trop longue.

IV-G-2 - Commentaires

Plus vous écrirez de programmes complexes, plus l'usage de commentaires se révélera important :

- les commentaires ne doivent pas nécessairement être longs. Ils doivent juste expliquer clairement l'objectif du code ;

- chaque fonction devrait avoir un commentaire qui stipule ce que fait la fonction. Exceptionnellement, vous pouvez omettre le commentaire si la fonction est si simple que son usage est évident ;

- dans le cas du carnet d'adresses :

- les méthodes initialize, ajoute et retire n'ont pas besoin d'être commentées,

- dans le code de la classe, les méthodes chaque_personne et chaque_adresse ont un seul commentaire, qui stipule que ce sont des itérateurs. Une fois que le programmeur sait que ces méthodes sont des itérateurs, il devine plus facilement l'usage de ces méthodes,

- même chose pour la méthode par_nom, dont le commentaire explique au lecteur qu'il s'agit d'une fonction de tri ;

- les commentaires peuvent également être utilisés pour grouper ensemble une série de fonctions communes. Par exemple, dans le carnet d'adresses, nous avons utilisé des commentaires pour rassembler les « méthodes fondamentales », « itérateurs » et « fonctions de tri ».

Essayez toujours de diviser le code en petites parties, plus facilement maintenables.

V - Obtenir de l'aide

V-A - Support technique

Nombreux sont les développeurs qui ont choisi Ruby comme langage de prédilection! Et ce nombre tend à croître intensivement avec le temps. Ce qui forme une communauté fort soudée, toujours prête à vous aider, quel que soit le problème.

- La liste de diffusion officielle de Ruby est probablement le meilleur moyen pour trouver de l'aide. Les abonnés répondent rapidement, et ce d'une façon très amicale. Pour vous y inscrire, il suffit d'envoyer un e-mail à ruby-talk-ctl@ruby-lang.org, en spécifiant le message suivant dans le corps de l'e-mail : « subscribe Votre-Prénom Votre-Nom ».

Cette liste est très active. Vous recevrez une quantité non négligeable d'e-mails par jour.

Les discussions se font uniquement en anglais. Néanmoins, il existe une liste de diffusion francophone. Pour s'inscrire, il faut envoyer à l'adresse ruby-fr-ctl@ruby-lang.orgle même style de message que pour la liste anglophone. Malheureusement, cette liste n'est pas très active…

- IRC : si vous avez un client IRC, vous pouvez obtenir de l'aide directement via le canal de discussion Ruby officiel. Connectez-vous simplement sur le réseau freenode, et joignez le canal #ruby-lang.

Si vous ne savez pas ce qu'est IRC, alors ce siteest fait pour vous.

- Groupes d'utilisateurs Ruby : il existe peut-être un groupe d'utilisateurs Ruby près de chez vous. Dans ce cas, il s'agit souvent d'un moyen intéressant pour avoir de l'aide sur Ruby, ainsi que de rencontrer par la même occasion d'autres passionnés.

V-B - Documentation

En plus de ce tutoriel, vous pouvez trouver d'autres documents utiles sur le langage Ruby.

V-B-1 - Autres tutoriels

Chris Pine à également écrit un tutoriel d'introduction à Ruby : Apprenez à programmer(disponible en anglais uniquement).

L'auteur a adopté une approche différente de la mienne, pour couvrir pratiquement le même contenu. Je vous suggère d'y jeter un coup d'œil.

V-B-2 - Livres

Voici quelques livres se focalisant sur Ruby. Remarquez, je n'ai pas lu les deux premiers bouquins. Mes commentaires sont donc basés sur ce que j'ai pu entendre via la communauté.

- Sams Teach Yourself Ruby in 21 Days(de Mark Slagell) : Il s'agit du livre le plus accessible dans cette liste. Le lecteur n'a besoin d'aucune connaissance particulière pour aborder cet ouvrage. L'auteur adopte un style « tutoriel » pour enseigner toutes les fonctionnalités importantes du langage, que ce soit les bases ou même certains points avancés. Ce livre a été très bien accepté par la communauté. C'est probablement un bon bouquin.

- The Ruby Way(de Hal Fulton) : Ce livre est basé sur une approche « livre de cuisine ». Vous y trouverez un bon nombre de solutions pour un large panel de problèmes fréquemment rencontrés. Il s'agit d'un livre d'une difficulté moyenne, et il est très souvent recommandé par la communauté.

- Programming Ruby(de David Thomas) : Ce livre est une référence complète du langage Ruby. Il est souvent représenté sous le nom de « PickAxe » (qui peut se traduire en français par « piolet », une sorte de pic à glace), simplement parce qu'il s'agit de l'illustration principale sur la couverture du livre. C'est de loin l'ouvrage le plus souvent mentionné dans la liste de diffusion Ruby. Il s'agit d'une excellente référence.

Ce livre est un peu plus avancé que ce tutoriel. Le lecteur doit avoir quelques connaissances préalables en matière de programmation, et doit comprendre l'architecture orientée objet. Il est disponible gratuitement en ligne sur le site rubycentral.

Ces livres ne sont disponibles qu'en anglais.

VI - Installer Ruby sur votre machine

Voici quelques brèves instructions concernant l'installation de Ruby sur plusieurs systèmes différents. Si vous rencontrez des problèmes, n'hésitez pas à demander de l'aide sur la liste de diffusion (Annexe A).

VI-A - GNU/Linux

La plupart des distributions GNU/Linux fournissent Ruby dans l'installation par défaut. Néanmoins, si Ruby n'est pas présent sur votre machine, voici ce qu'il faut faire pour l'installer en fonction de votre système.

VI-A-1 - Installation par RPM (Redhat, Mandrake…)

Vous pouvez télécharger un paquetage RPM sur RPM Findet l'installer via la commande rpm -Uhv ruby-*-.rpm.

VI-A-2 - Gentoo

Ruby fait partie de portage. Un simple emerge rubyfera le boulot à votre place.

VI-A-3 - Debian

Ruby s'installera sur votre Debian comme n'importe quel autre logiciel, en invoquant apt-get install ruby.

VI-B - Mac OS X

Ruby est distribué par défaut avec Mac OS X. Si vous le désirez, vous pouvez obtenir un binaire sur le site de téléchargement d'Apple.

VI-C - FreeBSD

Ruby fait partie de la collection des ports de FreeBSD. Vous pouvez donc l'installer en utilisant les instructions suivantes :

# cd /usr/ports/lang/ruby
# make
# make install
# make clean

VI-D - Microsoft Windows

À l'heure où nous écrivons ces lignes, la dernière version disponible de Ruby pour Microsoft Windows est 1.6.8. Il existe un installateur Microsoft Windows qui se chargera de copier Ruby sur votre système, vous pouvez le télécharger ici. Vous pouvez toujours trouver la dernière version de cet installeur sur cette page.