Apprendre Ruby pas à pas en pdf


Télécharger Apprendre Ruby pas à pas en pdf
33 étoiles sur 5 a partir de 1 votes.
Votez ce document:

Télécharger aussi :



C’est un langage de script orienté objet et interprété.

Il effectue directement les appels système en traitant les chaines de caractères au moyen de fonctions développées et des expressions régulières.

Il est non déclaratif et non typé.

Sa syntaxe est simple et se rapproche de celle de langages de scripts existants.

Il est orienté objet.

Pour vérifier que l’interpréteur est présent sur le système, il suffit de taper la commande : ruby -v

Welcome to Darwin! [coruscant:~] chris% ruby -v

ruby 1.6.7 (2002-03-01) [powerpc-darwin6.0]

[coruscant:~] chris%  

L'option -e permet d’exécuter un programme Ruby sur une seule ligne (one liner).

[coruscant:~] chris% ruby -e 'print "Bonjour a tous\n"'

Bonjour a tous

[coruscant:~] chris%

Traditionnellement, un programme Ruby sera stocké dans un fichier auquel l’interpréteur fera référence. Nous allons éditer un fichier () et y stocker le programme vu précédemment :

print "Bonjour a tous\n"

puis l’exécuter au moyen de la commande : ruby

[coruscant:~/ruby] chris% ruby

Bonjour a tous

[coruscant:~/ruby] chris%

Nous venons d’entrer dans le monde de Ruby.

Nous allons mettre en évidence une particularité de ruby qui est d’effectuer des calculs avec des entiers de précision infinie. Pour cela, nous allons écrire un programme récursif permettant de calculer n!

En Ruby, cette fonction s’écrit :

def fact(n)   if n == 0     1   else     n * fact(n-1)

  end

end

Contrairement à Perl ou Javascript, Ruby me matérialise pas la fonction comme un bloc entre accolades.

Par contre, comme Perl, il renvoie systématiquement comme valeur de retour la dernière valeur évaluée. Le programme permettant de calculer et d’afficher la factorielle d’un nombre est :

def fact(n)   if n == 0

    1   else

n * fact(n-1)

  end end n=5

print fact(n),"\n"

Ce texte sera sauvegardé dans un fichier et exécuté.

[coruscant:~/ruby] chris% ruby

120

[coruscant:~/ruby] chris%

Transformons légèrement ce programme afin qu’il récupère son argument sur la ligne de commande.

Comme dans le cas de Perl, l’argument se retrouvera dans une liste nommée ARGV[0]. La fonction to_i permet par ailleurs de convertir en entier une chaîne de caractères. Le nouveau programme fac devient :

def fact(n)   if n == 0

    1   else

n * fact(n-1)

  end end n= ARGV[0].to_i print fact(n),"\n"

Et son exécution donne comme résultat :

[coruscant:~/ruby] chris% ruby 5

120

[coruscant:~/ruby] chris%

Explications :

def fact(n)

Def  est la déclaration qui va nous permettre de définir une méthode. Elle s’appellera fact et aura un argument , la variable désignée sous le nom de n.

if n==0

L’instruction if va permettre de tester une condition, ici le fait que la valeur de la variable n est égale à  zéro (n==0).

1

Si c’est le cas, alors cette ligne sera évaluée. La valeur renvoyée par la fonction étant  systématiquement la dernière valeur évaluée, la valeur 1 sera retournée.

else

n * fact(n-1)

La valeur de la variable n est différente de zéro, on rappelle la méthode fact afin de calculer n * (n-1) !

end

Le premier end termine l’instruction if.

end

Le second end termine la fonction.

n=5

Ici commence le programme proprement dit. On définit une variable qui contient la valeur de la factorielle à calculer.

print fact(n),"\n"

On en demande l’impression.

Notons au passage que la seconde version du programme lisait la valeur de l’argument sur la ligne de commande. Les instructions étaient  :

n= ARGV[0].to_i

Ici comme en perl, ARGV est une liste qui contient l’ensemble des arguments de la ligne de commande. Ces arguments étant récupérés sous la forme de chaîne de caractères, il est indispensable pour  effectuer un calcul de procéder à une conversion, d’où la méthode to_i qui transforme une chaîne de  caractères en une valeur entière.

print fact(n),"\n"

On en demande l’impression.

Comme dans le cas de perl, les arguments de la ligne de commande se récupèrent par l’intermédiaire de la liste ARGV, par contre, contrairement à perl, la conversion chaîne de caractères <-> entier n’est pas automatique. D’où l’utilisation de la méthode to_i qui permet de procéder à cette conversion.

Un code bien écrit doit être documenté, les annotations dans les marges sont à ce sujet fondamentales car elles permettent

de mettre en évidence le travail effectué par le programme ligne par ligne si nécessaire.

Nul n’est capable de comprendre le code écrit par une tierce personne. L’auteur lui même peut avoir du mal à relire sa réalisation et à retrouver l’état d’esprit dans lequel il se trouvait au moment de la conception.

Il est aussi important de noter que le remède peut être pire que le mal, un commentaire en contradiction avec le code, ou placé au mauvais endroit peuvent être pires que pas de commentaires du tout.

Par définition, le code se doit d’être clair. Il ne faut pas croire que le commentaire est la panacée et sera toujours le remède à un code incompréhensible.

La convention utilisée par ruby est commune aux langages de script :

Undièse (#) indique le début d'un commentaire, tout ce qui le suit jusqu'à la fin de la ligne sera ignoré par l'interpréteur.

Les commentaires longs, sur plusieurs lignes, seront pour leur part encadrés par les deux lignes :

=begin

. . .

=end

print "Debut du programme.\n" print "Commentaire sur une ligne.\n" # Cette ligne est un commentaire

print "Commentaire sur plusieurs lignes.\n"

=begin

-------------------------------------

Commentaire du programme ecrit pour  mettre en evidence le fait que cette suite  de ligne est ignoree par l'interpreteur.

-------------------------------------

=end

print "Fin du programme.\n"

Exécution :

[coruscant:~/ruby] chris% ruby Debut du programme.

Commentaire sur une ligne.

Commentaire sur plusieurs lignes.

Fin du programme.

[coruscant:~/ruby] chris%

C’est un petit programme qui fait partie de la distribution de Ruby, it est écrit en Ruby et il permet une saisie de code directement au clavier en affichant le résultat au fur et à mesure.

Si vous ne disposez pas de ce programme, en voici le source :

line = ''

 indent=0 print "ruby> "  while TRUE

   l = gets    if not l

break if line == ''    else      line = line + l      if l =~ /,\s*$/ print "ruby| "        next      end

if l =~ /^\s*(class|module|def|if|case|while|for|begin)\b[^_]/        indent += 1      end

if l =~ /^\s*end\b[^_]/        indent -= 1      end

if l =~ /\{\s*(\|.*\|)?\s*$/        indent += 1      end      if l =~ /^\s*\}/        indent -= 1      end      if indent > 0        print "ruby| "        next      end    end    begin

     print eval(line).inspect, "\n"

   rescue

     $! = 'exception raised' if not $!      print "ERR: ", $!, "\n"

   end    break if not l

   line = ''    print "ruby> "

 end

 print "\n"

Il vous suffit de l’éditer, de le sauver sous le nom eval .rf et de le soumettre à l’interpréteur. C’est cette facilité que nous allons utiliser maintenant. Ainsi, si nous reprenons le premier programme que nous avons réalisé : print "Bonjour a tous\n" au moyen de cet outil, nous obtenons :

[coruscant:~/ruby] chris% ruby ruby> print "Bonjour a tous\n"

Bonjour a tous

nil

ruby> exit

[coruscant:~/ruby] chris%

L’instruction print va générer la ligne : Bonjour a tous. 

Le nil de la ligne suivante représente la dernière évaluation réalisée.

Comme il n’y a pas de distinction entre les instructions et les expressions, évaluer un bout de programme revient à l'exécuter comme c’est le cas pour Perl.

Le nil indique que l’instruction print n’a pas rendu de valeur significative. 

A remarquer l’invite de commande "ruby>".

Par la suite, seront indifférament utilisées les présentations au moyen de cet outil ou les programmes édités sur fichier et

exécutés directement par l’interpréteur.

Les programmes nécessitant un source un peu long seront de préférence présentés conne un fichier soumis à l’interpréteur.

Les démonstrations simples qui ne demandent que peu de lignes de code seront soumises à . 

Une chaîne de caractères est un ensemble de caractères qui se présente entre deux simples quottes ou entre deux doubles quottes.

ruby> "Bonjour a tous" "Bonjour a tous"

ruby> 'bonjour tout le monde' "bonjour tout le monde" ruby>

Comme dans Perl, dans une chaîne située entre doubles quottes le mécanisme de substitution est activé, c’est à dire que tous les caractères d’échappement seront interprétés, alors que les simples quottes désactivent le mécanisme de substition.

ruby> print "Bonjour\nMonsieur\n"

Bonjour Monsieur

nil

ruby> print 'Bonjour\nMonsieur\n',"\n"

Bonjour\nMonsieur\n nil

ruby> '\n' "\\n" ruby> "\n"

"\n"

ruby> "Il y a #{15*3} personnes"

"Il y a 45 personnes" ruby> var="Bonjour"

"Bonjour"

ruby> "#{var} Madame." "Bonjour Madame." ruby>

Comme Javascript, Ruby propose la concaténation de chaînes au moyen de l’opérateur + et comme Perl in permet

d’effectuer la multiplication de chaînes au moyen de l’opérateur *.

ruby> "Bonjour "+"Monsieur"

"Bonjour Monsieur" ruby> "Trois " * 3 "Trois Trois Trois " ruby> a="Bon"

"Bon"

ruby> b="jour"

"jour" ruby> c=" "

" "

ruby> d="Monsieur."

"Monsieur." ruby> salut=a+b+c+d "Bonjour Monsieur." ruby>

Il est aussi possible de jouer avec la multiplication des chaînes. Soit à générer la chaine « aaaa bbb ».

Cette chaine est constituée de la chaîne « a » répétée quatre fois, suivie de la chaine espace (« ») suivie de la chaine « b » répétée trois fois.

Il ne reste donc qu’à réécrire en ruby ce que nous venons de dire pour obtenir le résultat souhaité.

ruby> vara="a"

"a"

ruby> varb="b"

"b"

ruby> ch=vara * 4 + " " + varb * 3

"aaaa bbb" ruby> 

Par ailleurs, Ruby considère les caractères comme des entiers rendant ainsi possible l’indexation d’une chaime de caractères. La numérotation des indices commençant à 0.

ruby> mot="Bonjour"

"Bonjour" ruby> mot[0]

66

ruby> mot[1]

111 ruby>

Le code ASCII du caractère B est bien 66 (42 hexadécimal) et celui de o est bien 111 (6F hexadécimal).

Par ailleur, il est intéressant de noter que, si l’indice est positif, il indique une exploration de la chaîle de la gauche vers la droite, alors que si les indices sont négatifs, l’exploration de la chaîne se fait de la droite vers la gauche.

L’extraction d’une sous chaîne se fait simplement en indiçant la chaîne par plusieurs valeurs.

Dans l’exemple qui suit, nous partons d’une chaine contenue dans une constante puis nous en extrayons des sous chaines successives.

Il faut bien noter que la première sous chaine qui est récupérée chiffres[3] donne comme résultat 51 (33 hexadécimal) correspondant au code ASCII du chiffre 3 alors que les sous chaines comportant plus d’un élément donnent comme résultat les caractères correspondant.

Ceci est du à la remarque qui a été faite plus haut, à savoir que les caractères représentant les éléments d’une chaine sont considéres comme des entiers.

ruby> chiffres="0123456789"



"0123456789" ruby> x=chiffres[3]

51

ruby> zero=chiffres[0,1]

"0"

ruby> huitneuf=chiffres[-2,2]

"89"

ruby> uncinq=chiffres[1..5]

"12345"

ruby> unsix=chiffres[1..-4]

"123456"

ruby> vide=chiffres[-2..3] nil

ruby> deuxcinq=chiffres[-8..-5]

"2345" ruby>

Comparaisons de chaînes.

false

ruby> chiffres[-8..-5]==chiffres[2,4] true

ruby> chiffres[1]==chiffres[-1] false ruby>

Et maintenant nous allons réaliser un programme afin de jouer avec la machine.

Cette dernière va choisir un nombre et nous devons le deviner en posant des questions successives. Les réponses qui nous serons fournies seront « Plus grand » si le nombre que nous proposons est inférieur à celui qu’à choisi la machine, plus petit dans le cas contraire.

Dés que le nombre aura été trouve, le programma se termine.

Ce programme sera réalisé comme tout programme source au moyen d’un éditeur de texte. Il sera ensuite stocké dans un fichier afin d’être soumis à l’interpréteur ruby.

Le programme aura l’allure suivante :

#Programme secret = rand(255)

print "quel est le nombre inferieur a 256 auquel je pense? " while proposition = !   if proposition.to_i == secret     print "Bravo, tu as gagne!\n"     break   else

    if proposition.to_i > secret       print "Plus petit!\n"     else

print "Plus grand!\n"

    end     end

  print "On recommence? " end

Son exécution donne le résultat :

[coruscant:~/ruby] chris% ruby

quel est le nombre inferieur a 256 auquel je pense? 100 Plus petit!

On recommence? 50

Plus petit!

On recommence? 25 Plus grand!

On recommence? 30 Plus grand!

On recommence? 40 Plus grand!

On recommence? 45

Plus petit!

On recommence? 42 Plus grand!

On recommence? 43 Plus grand!

On recommence? 44 Bravo, tu as gagne!

[coruscant:~/ruby] chris%

Ce programme met en évidence l’utilisation de la boucle while.

Les instructions présentes entre les deux mots clé while et end seront répétées tant que le condition spécifiée sera vraie.

Dans le programme que nous allons détailler ci dessous, cette condition est représentée par la lecture d’une chaîne de

caractères sur l’entrée standard.

Ruby, lorsqu’on lui demande l’évaluation en tant que valeur logique d’un scalaire (chaîne de caractères ou valeur

numérique), renvoie « faux » si il s’agit d’une chaîne vide ou de la valeur numérique 0 et « vrai » dans tous les autres cas.

Il faut noter que Ruby comme Perl lit tous les caractères qui lui sont proposée sur <STDIN>,  le caractère de fin de ligne fait donc partie intégrante de la ligne lue.

Ainsi, une ligne pour laquelle l’utilisateur n’aura donné aucune information à l’exception du « retour » ne sera pas vide car elle contiendra le caractère \n.

Seule la ligne contenant le caractère de fin de fichier (^D) sera physiquement vide et donc évaluée à « faux ».

Ainsi, l’usager qui ne veut plus jouer pourra terminer le programme de manière anticipée en tapant ^D en lieu et place d’une valeur numérique.

  [coruscant:~/ruby] chris% ruby

quel est le nombre inferieur a 257 auquel je pense? 100 Plus petit!

On recommence? [coruscant:~/ruby] chris%

Une autre remarque sur l’utilisation de la méthode chop. Comme en perl, la méthode chop permet de supprimer le dernier caractère d’une chaîne (généralement le \n).

En ruby, certaines méthodes peuvent se terminer par un point d’exclamation ! ou un point d’interrogation ?.

Le point d’exclamation que l’on prononce « bang » met en évidence une méthode potentiellement destructrice qui va altérer la valeur de l’objet concerné.

En résumé, la méthode chop! Va directement modifier la chaîne concernée alors que la méthode chop (sans le bang) va travailler sur une copie de l’objet.

ruby> c="abcdef"

"abcdef" ruby> c.chop! "abcde" ruby> c "abcde"

ruby>

"abcd" ruby> c "abcde" ruby> resultat "abcd" ruby>

Par ailleurs, une méthode qui se termine par un point d’interrogation que l’on prononce « euh » indique qu’il s’agit d’une méthode de type prédicat dont la valeur de retour est soit « vrai » soit « faux ».

secret = rand(255)

Ici, on tire un nombre aléatoire dans l’intervalle 0..255

print "quel est le nombre inferieur a 256 auquel je pense? "

Puis on pose la question à l’usager.

while proposition =

La boucle while prend comme condition la méthode qui permet de lire une chaîne de caractères sur  l’entrée standard (<STDIN>) effectuera une lecture  tant que la chaîne ne sera pas vide.

!

Comme la chaîne de caractères qui vient d’être lue Contient le caractère de fin de ligne (\n)on le supprime au moyen de la méthode chop.

if proposition.to_i == secret

On teste alors la valeur du nombre proposé par

Rapport a celui qui a été choisi par le programme.

print "Bravo, tu as gagne!\n"

Si les deux nombres sont égaux, c’est gagné. On le dit

break

Et on termine le programme.

else

Dans le cas contraire.

if proposition.to_i > secret

On donnera à l’usager une indication sur la place du Nombre qu’il vient de proposer par rapport à celui Qu’il doit découvrir.

print "Plus petit!\n"

Soit il est plus petit.

else

print "Plus grand!\n"

Soit il est plus grand.

end

Fin du if interne (if proposition.to_i > secret)

end

Fin du if externe (if proposition.to_i == secret)

print "On recommence? "

Le nombre n’a pas été trouvé, on repose la question.

end

Fin du while.

Comme Perl ou Javascript, Ruby traite les expressions régulières. Le but étant de tester si une chaîne de caractères correspond à un certain modèle.  Un certain nombre de caractères ont une signification bien spécifique, ce sont :

Caractère

Signification

[ ]

spécification d'intervalle (par ex: [a-z] indique une lettre dans l'intervalle a à z)

\w

lettre ou chiffre; équivaut à [0-9A-Za-z]

\W

ni lettre ni chiffre

\s

caractère espace; équivaut à[ \t\n\r\f]

\S

caractère non espace

\d

chiffre; équivaut à [0-9]

\D

non-chiffre

\b

backspace (0x08) (seulement dans un intervalle)

\b

limite de mot (sauf dans un intervalle)

\B

limite autre que de mot

*

zero, 1 ou n occurrences de ce qui précède

+

1 ou n occurrences de ce qui précède

{m,n}

au moins m et au plus n occurrences de ce qui précède

?

Au plus une occurrence de ce qui précède; équivaut à {0,1}

|

alternative: soit ce qui précède soit ce qui suit

( )

groupe

En ruby, comme en Perl, une expression régulière est généralement encadrée par des slashs (/). 

Par ailleurs, pour soumettre une expression régulière à une variable contenant une chaîne de caractères, on utilise un opérateur spécifique =~ qui fournira comme réponse l’emplacement du début du modèle dans la chaine si il a été repére ou nil dans le cas contraire.

Soit par exemple à chercher une chaîne construite sue le modèle suivant :

? Elle doit commencer par une lettre de l'intervalle a-d (a,b,c ou d)

? Puis doivent apparaître les deux lettres 'hr'

? Ensuite doit être présente une lettre prise dans l'intervalle e-i ou dans l'intervalle x-z (e,f,g,h,i,x,y ou z)

Le modèle de recherche de cette chaîne sera : /^[a-d]\w*hr\w*[e-ix-z]/

ruby> def expr(mot)

ruby| (mot =~ /^[a-d]\w*hr\w*[e-ix-z]/) != nil ruby| end

nil

ruby> expr "bonjour"

false

ruby> expr "chrysantheme" true

ruby> expr "chrysalide"

true ruby>

Rappelons qu’un nombre hexadécimal se représente sous la forme Oxhh…h, c’est à dire le chiffre 0 suivi de la lettre x en majuscule ou en minuscule, suivi du nombre hexadécimal dans lequel les lettres peuvent apparaître indifféremment en majuscule ou en minuscule.

L’expression régulière qui validera la représentation d’un nombre hexadécimal est : /0[xX][0-9A-Fa-f]+/

ruby> def hexa (val)

ruby| (val =~ /0[xX][0-9A-Fa-f]+/) != nil ruby| end

nil

ruby> hexa "2002"

false

ruby> hexa "0X20G34" true

ruby> hexa "0x20FAB4DC" true ruby> 


Une liste est un ensemble de scalaires compris entre deux crochets [ ] et séparés par des virgules.

ruby> liste = [1,2,3,4,5,6,7,8,9]

[1, 2, 3, 4, 5, 6, 7, 8, 9] ruby>

Une liste peut êtr concaténée à une autre liste, multipliée par un scalaire exactement comme pour une chaîne de caractères.

ruby> chiffres = [0,1,2,3,4,5,6,7,8,9]

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

ruby> voyelles = ["a","e","i","o","u"]

["a", "e", "i", "o", "u"]

ruby> liste = chiffres + voyelles

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "e", "i", "o", "u"]

ruby> liste = voyelles * 2

["a", "e", "i", "o", "u", "a", "e", "i", "o", "u"] ruby>

Une liste est indicée de la même manière que dans perl au moyen d’un entier liste[i].

ruby> chiffres = [0,1,2,3,4,5,6,7,8,9]

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

ruby> chiffres[1]

1

ruby> chiffres[3..8] [3, 4, 5, 6, 7, 8] ruby> chiffres[-2,2]

[8, 9]

ruby> chiffres[-2..-1]

[8, 9]

ruby>

Comme pour les chaînes de caractères, les indices négatifs indiquent un déplacement à partir de la fin de la liste.

Les deux opérateurs split et join permettent comme en perl ou en javascript de convertir une chaîne de caractères en une liste ou inversement de concaténer les élements d’une liste pour former une chaîne de caractères.

ruby> chiffres = [0,1,2,3,4,5,6,7,8,9]

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ruby> chaine = ("-")

"0-1-2-3-4-5-6-7-8-9" ruby> ch = chaine.split("-")

["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] ruby>

C’est une méthode d’accès spécifique aux éléments d’un ensemble. Chaque élément est repéré, non pas par un indice spécifiant son emplacement dans une liste ordonnée, mais par une clé spécifique.



La définition d ‘un hash diffère quelque peu de ce qui a été vu, tant en Perl que en javascript.

Il est important de ne pas considérer un hash comme une table ou une liste au sens ou nous venons de le voir.

Le repérage d’un objet se fait au moyen d’une clé calculée à partir de la chaine de caractères qui a permis l’identification de la valeur.

La notion d’ordre, au sens liste du terme, n’apparaît dons pas dans le cas d’un hash.

ruby> couleur = {"rouge"=> 100, "vert"=> 175, "bleu"=> 150}

{"bleu"=>150, "rouge"=>100, "vert"=>175} ruby> couleur["bleu"]

150

ruby> couleur["vert"]

175

ruby> couleur["rouge"]

100

ruby> couleur

{"bleu"=>150, "rouge"=>100, "vert"=>175} ruby> 

Ajouter une valeur.

ruby> couleur

{"bleu"=>150, "rouge"=>100, "vert"=>175} ruby> couleur["jaune"] = 90

90

ruby> couleur["mauve"] = 50

50

ruby> couleur

{"jaune"=>90, "bleu"=>150, "rouge"=>100, "mauve"=>50, "vert"=>175} ruby>  

Ainsi qu’il a été dit, l’ordre dans lequel les divers éléments apparaissent n’est absolument pas celui dans lequel ils ont été introduits.

Nous allons détailler maintenant les structures de contrôle dont dispose le langage.

Cette instruction permet de tester une suite de conditions. Elle a tendance a ressembles au switch de java mais permet de faire beaucoup plus de choses.

ruby> i=8 8 ruby> case i ruby| when 1,3,5,7,9 ruby|   print "Chiffre impair\n"

ruby| when 2,4,6,8 ruby| print "Chiffre pair\n" ruby| end Chiffre pair nil

ruby> 

Pour comprendre le fonctionnement précis de la structure case, il est indispensable de présenter et de définir le fonctionnement d’un opérateur spécifique ===.

Cet opérateur est un opérateur relationnel permettant te tester plusieurs objets simultanément.

En restant dans l’orientation objet, === aura une interprétation qui sera variable en fonction de la nature de l’objet.

Prenons par exemple le programme suivant et exécutons le.

print "Donnez moi une chaine de caracteres : " while proposition = !   case proposition     when 'bonjour'

    print "La chaine est en minuscules.\n"

    when /[BONJOUR]/

       print "Elle contient des majuscules.\n"      end

  print "Donnez moi une chaine de caracteres : " end

Nous obtenons le résultat suivant :

[coruscant:~/Ruby] chris% ruby Donnez moi une chaine de caracteres : bonjour La chaine est en minuscules.

Donnez moi une chaine de caracteres : boNjour Elle contient des majuscules.

Donnez moi une chaine de caracteres : ^D

[coruscant:~/Ruby] chris%

Case, nous venons de le dire, utilise l’opérateur relationnel pour effectuer ses tests.

Dans ces conditions, le premier test when ‘bonjour’ teste si la chaine contenue dans la variable considérée est exactement le

chaîne de caractères ‘bonjour’.

Par contre, pour ce qui est du second test  when:/[BONJOUR]/ l’interprétation qui en est faite est le test de la réussite ou de l’échec de l’application d’une expression régulière.

Il est ainsi possible de tester si une valeur est comprise dans un intervalle donné.

ruby> i=3

3

ruby> (1..5)===i

true ruby>

C’est un moyen simple de construire des boucles non bornées.

Nous en avons déjà vu l’utilisation lors des boucles de lecture.

Le corps de la boucle sera effectué tant que la condition spécifiée dans le while sera vraie.

  while condition

  instruction 1      instruction 2

                             .

                             .

                             .

  instruction n  end

Comme en perl, while peut servir pour contrôler une unique instruction.

ruby> i=0 0

ruby> print "#{i+=1}\n" while i<5

1

2

3

4

5

nil

ruby>

Permet de tester une condition négative.

Le corps de la boucle sera effectué tant que la condition spécifiée dans le until sera fausse.

  until condition

  instruction 1      instruction 2

                             .

                             .

                             .

  instruction n  end

De la même manière que unless est le complément du until, unless est l’instruction complémentaire du if. Elle permettra d’exécuter le contenu d’un bloc si la condition spécifiée est fausse.

  unless condition

  instruction 1      instruction 2

                             .

                             .

                             .

                             .

  instruction n  end

La boucle for permet d’explorer les divers objets d’un ensemble en effectuant une itération pour chacun d’entre eux.  for i in ensemble

  instruction 1      instruction 2

                             .

                             .

                             .

                             .

  instruction n  end

ruby> for i in (1..5) ruby|   print i,"\n" ruby| end

1

2

3

4

5 1..5 ruby>

L’ensemble spécifié peut être n’inporte quel objet.

ruby> for i in [1,5..7,3.14,"alpha"]

ruby|   print i,"\n" ruby| end

1

5..7 3.14 alpha

[1, 5..7, 3.14, "alpha"] ruby>

Profitons de cet exercice pour introduire une nouvelle notion. Il est possible en ruby de tester le type d’une variable au moyen de la méthode type. Ainsi, si i est in entier, i.type va retourner le chaîne de caractères ‘fixnum’.

Nous pouvons appliquer cette méthode aux divers éléments de la boucle for que nous venons d’écrire.

ruby> for i in [1,5..7,3.14,"alpha"]

ruby| print i,"\t(",i.type,")\n"

ruby| end

1           (Fixnum)

5..7 (Range) 3.14 (Float) alpha (String)

[1, 5..7, 3.14, "alpha"] ruby>

L’instruction print fait apparaître une tabulation (\t). Cette instruction aurait pu s’écrire aussi : print "#{i}\t(#{i.type})\n"  

Comme en perl et en javascript, une boucle peut être interrompue en cours d’exécution. Ruby nous propose quatre moyens d’effectuer cette rupture.

break      pour terminer la boucle. next      pour terminer l’itération courante et en commencer une nouvelle. redo      recommence l’itération courante. return termine simultanément la boucle courante, et la méthode qui la contient en retournant un argument. 

Un itérateur est un concept courant dans les langages orienté objet. Nous avons vu comment au moyen d’une boucle for on pouvait procéder à l’exploration des divers éléments d’un ensemble. Cette exploration est aussi possible au moyen des itérateurs.

Exemple, pour une chaîne de caractères, l’itérateur permettra de procéder à la récupération de chacun des

caractères constituant la chaîne en question.

Exemple avec le type string

ruby> "abc".each_byte{|x| printf "<%c>", x}; print "\n"

<a><b><c>

nil

ruby>

each_byte est une méthode qui va permettre de procéder à une suite d’itérations sur les divers éléments qui constituent la

chaîne de caractères. Chacun etant stocké dans la variable locale x.

En fait, cette même opération aurait très bien pu s’écrire sous une forme plus classique :

ruby> s="abc"    

"abc" ruby> i=0

0

ruby> while i<s.length ruby|   printf "<%c>\n", s[i]       ruby|   i=i+1 ruby| end

<a>

<b>

<c>

nil

ruby>

Il faut toutefois remarquer que l’itérateur est plus simple d’utilisation et pourra fonctionner même si la classe String doit être ultérieurement modifiée.

Un autre itérateur, each_line va explorer une chaîne en considérant comme séparateur le \n représentatif de la fin de ligne.

ruby> t="Un\nDeux\nTrois\nQuatre\nCinq\n" "Un\nDeux\nTrois\nQuatre\nCinq\n" ruby> t.each_line{|x| print x}

Un

Deux

Trois

Quatre

Cinq

"Un\nDeux\nTrois\nQuatre\nCinq\n" ruby>

En fait, tout ce qui, dans les langages traditionnels se fait au moyen d’itérations classiques peut être réalisé plus simplement au moyen des itérateurs.

Notons au passage que, en ruby, l’instruction for réalise l’itération au moyen d’un each.

Or, appliqué à une chaîne, l’instruction each est «équivalente à un each_line.

Ainsi, la boucle :

 t ="Un\nDeux\nTrois\nQuatre\nCinq\n"  for x in t

   print x

 end

produira le résultat suivant :

ruby> t ="Un\nDeux\nTrois\nQuatre\nCinq\n"

"Un\nDeux\nTrois\nQuatre\nCinq\n" ruby> for x in t

ruby| print x

ruby| end Un

Deux

Trois

Quatre

Cinq

"Un\nDeux\nTrois\nQuatre\nCinq\n" ruby>

Le contrôle retry utilisé dans une boucle permet de rééxécuter l’itération courante à son début.

ruby> c="Faux"  

"Faux"

ruby> for i in 1..5 ruby|   print i

ruby| if i == 3 and c == "Faux"

ruby|     c = "Vrai" ruby|     print "\n" ruby|     retry ruby|   end

ruby| end; print "\n"

123 12345

nil

ruby>

La commande yield (fournir) permet de passer le contrôle au bloc de code qui est associé à l’itérateur dans lequel il ,est

défini.

Ainsi, dans l’exemple suivant, on définit un itérateur ‘repeter’ qui permet d’exécuter un bloc de code ; le nombre de répétitions étant la valeur passée en argument.

ruby> def repeter (n) ruby|    while n > 0 ruby|      yield ruby|      n-=1 ruby|    end

ruby| end

nil

ruby> repeter (3) {print "Bonjour\n"}

Bonjour

Bonjour

Bonjour

nil

ruby>

De la même manière, la commande ‘retry’ permet de construire un itérateur qui simule un while.

ruby> def tant_que (condition) ruby|    return if not condition ruby|    yield ruby|    retry ruby|  end

nil

ruby> i=0;tant_que (i<5) {print i; i+=1;print "\n"}

0

1

2

3

4

nil

ruby>

Ainsi donc, un itérateur est attaché à un certain type de données. Ce qui signifie que chaque fois qu’un nouveau type de données est défini, il est comode de définir en parallèle les itérateurs qui lui sont associés.

De manière classique, tout problème de programmation est vu sous l’angle de structures de données et des diverses procédures qui sont chargées de manipules ces structures. Dans ces conditions, les données sont passives.

Le problème vient du fait que la programmation est réalisée par des humains qui n’ont en tête à un instant donné qu’une vue limitée et parcellaire du problème global.

Lorsqu’un projet se développe, son noyau peut grossir démesurément au point qu’il deviendra vite impossible d’en assurer le suivi. C’est alors que de petites erreurs de programmation cachées font leur apparition dans le noyau. La maintenance peut alors tourner au cauchemar car toute correction d’une erreur est susceptible d’en provoquer une ou plusieurs nouvelles.

Lorsqu’on passe à la programmation orientée objet, on peut déléguer tout le travail fastidieux et répétitif aux données elles même. Le statut de la donnée passe donc de passif à actif. En quelque sorte, on cesse de considérer une donnée comme un objet sur lequel tout ou presque est permis, mais davantage comme une boite noire, hermétiquement close, sur laquelle on agit au moyen de boutons et d’interrupteurs, possédant des écrans de contrôle et de commande. Il sera impossible de déterminer de l’extérieur le niveau de complexité de cette boite. Il sera interdit de l’ouvrir que ce soit pour la consulter ou pour la modifier. Les seules interventions qui seront autorisées seront celles qui passeront par l’intermédiaire du panneau de commande. Ainsi, une fois la boite hermétiquement scellée, il ne sera plus nécessaire de savoir comment elle fonctionne.

D’un point de vue un peu simpliste, une méthode peut être définie comme la tâche qu’il est possible de demander à un objet d’accomplir. Comme en JavaScript ; la méthode d’un objet sera invoquée au moyen d’un point. L’objet etant à gauche et la méthode à droite.



ruby> x="abcdef"

"abcdef" ruby> x.length

6 ruby>

Intuitivement, on a créé un objet chaîne de caractères et on demande à cet objet de retourner sa longueur. On invoque la

méthode length appliquée à l’objet x.

Selon le type de l’objet auquel elle est appliquée, la méthode length aura des interprétations différentes, voire même aucune interprétation.

ruby> x="abcdef"

"abcdef" ruby> x.length

6

ruby> t=["abcd","123","xyz"] ["abcd", "123", "xyz"] ruby> t.length

3

ruby> z=1

1

ruby> z.length

:32: (eval):1: undefined method `length' for 1:Fixnum (NameError)

Manifestement, la méthode length appliquée à une chaine va retourner la longueur de la chaine en question, alors que la même méthode appliquée à une table va en retourner le nombre d’éléments.

Une erreur est générée qi la methode lengts est appliquée à un objet numérique.

ruby> t=["abcde","123456","wxyz"] ["abcde", "123456", "wxyz"] ruby> t.length

3

ruby> t[0].length

5

ruby> t[1].length

6

ruby> t[2].length

4 ruby> 

Un objet est donc capable d’interpréter une méthode en fonction de ce qu’il sait être. On appelle polymorphisme cette

propriété caractéristique des langages objets.

Nous avons aussi noté qu’une erreur sera générée dés qu’un objet reçoit une demande qu’il est incapable de satisfaire. Bien qu’il soit inutile de savoir comment une méthode donnée est exécutée, il est indispensable de savoir à tout moment si elle est ou non applicable à un objet donné.

Si la méthode nécessite une liste d’arguments, ils seront fournis entre parenthèses.

                     Objet.methode(argument1,argument2,….,argumentN)

Ruby possède une variable spéciale ‘self’ permettant de se référer à l’objet qui appelle la méthode.

En programmation orientée objet, on appellera classe toute catégorie d’objet. Les objets appartenant à une classe étant pour

leur part appelés instances de cette classe.

Fabriquer un objet c’est définir les caractéristiques de la classe à laquelle cet objet va appartenir, puis créer une instance.

ruby> class Homme ruby|   def nom

ruby|     print "Tout homme a un nom\n" ruby|   end

ruby| end

nil

ruby>

Nous définissons une classe simple Homme qui comporte une méthode simple nom.

ruby> #<Homme:0x146434>

ruby> 

La classe Homme étant créée, il est possible de l’utiliser pour fabriquer un homme spécifique que nous allons appeler Français . La méthode new s’applique à toute classe pour en créer une nouvelle instance.

L’instance Français de la classe Homme étant créée, il est maintenant possible de se référer aux méthodes qui ont été définies lors de la création de la classe :

ruby> Tout homme a un nom

nil

ruby>  

On appellera instanciation la création d’une nouvelle instance dans une classe donnée.

Il n’est pas possible de faire référence au constructeur de la classe. Ainsi 

ruby> class Homme ruby|   def nom   

ruby|     print "Tout homme a un nom\n" ruby|   end ruby| end

nil

ruby>

:32: (eval):1: undefined method `nom' for Homme:Class (NameError)

Par contre, il est possible pour tester les méthodes d’une classe de créer un objet éphémère anonyme qui disparaîtra aussitôt. 

ruby> class Homme ruby|   def nom

ruby|     print "Tout homme a un nom\n" ruby|   end

ruby| end

nil

ruby> ().nom Tout homme a un nom nil

ruby> Tout homme a un nom nil

En effet, n’ayant pas de nom, l’objet créé sera immédiatement détruit par le ramasse miettes (garbage collector) de l’interpréteur.

La classification des objets est généralement hiérarchique.

Si nous prenons l’exemple d’une classe que nous appellerions les mammifères. Il sera possible de subdiviser la classe des mammifères en sous classes, par exemple les humains, les primates et les quadrupèdes. Chacune de ces sous classes héritant des caractéristiques des mammifères. Par exemple, la caractéristique des mammifères est d’avoir des mamelles, cela signifie que toutes les sous classes héritent de cette propriété.

La sous classe représentative des primates aura par exemple en plus la caractéristique d’être des quadrumanes.

ruby> class Mammifere ruby|   def mammelle

ruby|     print"Un mammifere a des mammelles.\n" ruby|   end

ruby| end

nil

ruby> class Primate<Mammifere ruby|   def quadrumane

ruby|     print "Un primate a quatre mains\n" ruby|   end

ruby| end

nil

Nous avons ici défini une classe Mammifère et une méthode mamelle.

Dans cette classe mammifère nous définissons une sous classe Primate qui hérite des propriétés et des méthodes de la classe mammifère mais crée une nouvelle méthode quadrumane.

ruby>

#<Primate:0x146024>

Ainsi, l’objet gorille est un objet appartenant à la classe Primate elle même sous classe de la classe mammifère dont il

possède toutes les caractéristiques.

Il est donc tout aussi naturel d’appliquer à l’objet gorille la méthode mamelle, dont il a hérité de la classe mammifère :

ruby> gorille.mammelle Un mamlifere a des mammelles. nil

Ou la méthode quadrumane décrite dans la classe primate elle même.

ruby> gorille.quadrumane Un primate a quatre mains nil

Il existe toutefois des cas ou la sous classe ne doit pas hériter des caractéristiques de la classe supérieure.

Comme illustration, prenons une classe oiseau qui aura un certain nombre de caractéristiques dont le fait de voler. 

class Oiseau   def vole

    print "Un oiseau ca vole.\n"   end   def plumes

    print "Un oiseau ca a des plumes.\n"   end end

print "Appel de la methode :\n"

print "\nAppel de la methode Pigeon.plumes:\n" Pigeon.plumes

L’exécution de ce programme donne le résultat suivant :

[coruscant:~/ruby] chris% ruby Appel de la methode :

Un oiseau ca vole.

Appel de la methode Pigeon.plumes:

Un oiseau ca a des plumes.

[coruscant:~/ruby] chris%

Par contre, il existe des situations particulières pour lesquelles une sous-classe ne doit pas hérites des propriétés de la supae-classe. certaines propriétés de la super-classe. Ainsi par exemple, il existe une famille d’oiseaux coureurs (les ratites) qui ne savent pas voler, qui sont tout de même des oiseaux. Si nous reprenons la classe Oiseau que nous venons de voir et que nous désirons déclarer un oiseau, il va falloir demander à ce qu’il hérite de toutes les propriétés de la classe oiseau, mais redéfinir la méthode vole.

class Oiseau   def vole

    print "Un oiseau ca vole.\n"   end   def plumes

    print "Un oiseau ca a des plumes.\n"   end end

class Ratites<Oiseau   def vole

    print "Desole, je ne vole pas, je cours.\n"   end end

print "Creation de l'objet Pigeon ().\n"

print "Appel de la methode Pigeon.plumes:\n" Pigeon.plumes

print "\nAppel de la methode :\n"

print "\nCreation de l'objet Nandou ().\n"

print "Appel de la methode Nandou.plumes:\n" Nandou.plumes

print "\nAppel de la methode :\n"

Lorsqu’on l’exécute, ce programme donne le résultat suivant :

[coruscant:~/ruby] chris% ruby Creation de l'objet Pigeon ().

Appel de la methode Pigeon.plumes:

Un oiseau ca a des plumes.

Appel de la methode :

Un oiseau ca vole.

Creation de l'objet Nandou ().

Appel de la methode Nandou.plumes:

Un oiseau ca a des plumes.

Appel de la methode :

Desole, je ne vole pas, je cours.

[coruscant:~/ruby] chris%

Ainsi que nous venons de le voir, il est possible dans une sous-classe, de changer le comportement des instances en redéfinissant les méthodes de la super-classe.

Au lieu de de remplacer totalement la méthode de la super-classe il est aussi possible de la compléter.

class Oiseau   def vole

    print "Un oiseau ca vole.\n"   end   def plumes

    print "Un oiseau ca a des plumes.\n"   end end

class Ratites<Oiseau

  def vole super

    print "Oui, mais moi je ne vole pas, je cours.\n"   end

end

print "Creation de l'objet Pigeon ().\n"

print "Appel de la methode Pigeon.plumes:\n" Pigeon.plumes

print "\nAppel de la methode :\n"

print "\nCreation de l'objet Nandou ().\n"

print "Appel de la methode Nandou.plumes:\n" Nandou.plumes

print "\nAppel de la methode :\n"

Ce programme donne le résultat suivant :

Creation de l'objet Pigeon ().

Appel de la methode Pigeon.plumes:

Un oiseau ca a des plumes.

Appel de la methode :

Un oiseau ca vole.

Creation de l'objet Nandou ().

Appel de la methode Nandou.plumes:

Un oiseau ca a des plumes.

Appel de la methode :

Un oiseau ca vole.

Oui, mais moi je ne vole pas, je cours.

La directive ‘super’ permet aussi de passer de nouveaux arguments à la méthode originale.

Considérons le programme suivant.

Dans ce programme, nous faisons passer un argument à la méthode.

Cet argument, qfin de ne pas inutilement compliquer est la chaîne de caractères qui sera imprimée par l’ordre print qui constitue la méthode.

Dans ces conditions, l’appel de la méthode () doit impérativement présenter une liste d’appel.



495