• Aucun résultat trouvé

[PDF] Apprendre Ruby pas à pas en pdf | Formation informatique

N/A
N/A
Protected

Academic year: 2021

Partager "[PDF] Apprendre Ruby pas à pas en pdf | Formation informatique"

Copied!
66
0
0

Texte intégral

(1)RUBY. C. Aperghis-Tramoni Département d’Informatique.

(2) RUBY ............................................................................................................................................................. 1 . C. Aperghis-Tramoni................................................................................................................. 1 C’est quoi Ruby ? ...................................................................................................................... 4 Les débuts en programmation Ruby. ........................................................................................ 5 Les commentaires ...................................................................................................................... 7 Le programme eval.rb ............................................................................................................... 8 Les chaînes de caractères........................................................................................................ 10 Les expressions régulières....................................................................................................... 15 Les listes................................................................................................................................... 17 Les Hashes............................................................................................................................... 18 Structures de contrôle ............................................................................................................. 19 case.....................................................................................................................................................19 while...................................................................................................................................................20 until....................................................................................................................................................20 unless .................................................................................................................................................21 La boucle for.....................................................................................................................................21 Les ruptures de boucle.....................................................................................................................22 . Les itérateurs. .......................................................................................................................... 23 La programmation orienté-objet............................................................................................. 26 Les méthodes............................................................................................................................ 27 Les classes................................................................................................................................ 29 L’héritage................................................................................................................................. 31 Redéfinition des méthodes....................................................................................................... 34 Les contrôles d'accès ............................................................................................................... 37 Les méthodes Singleton........................................................................................................... 39 Les modules ............................................................................................................................. 40 Les objets procédures .............................................................................................................. 41 Les variables ............................................................................................................................ 43 Variables globales.................................................................................................................... 44 Variables d'instance ................................................................................................................ 45 Variables locales...................................................................................................................... 46 Constantes de classe ................................................................................................................ 49 Traitement des exceptions: rescue .......................................................................................... 51 Traitement des exceptions: ensure.......................................................................................... 55 Accesseurs................................................................................................................................ 56 .

(3) Initialisation des objets............................................................................................................ 61 Divers problèmes. .................................................................................................................... 64 Délimiteurs d'instruction.................................................................................................................64 Organisation du code. ......................................................................................................................64 . Et en guise de conclusion........................................................................................................ 66 .

(4) C’est quoi Ruby ? 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 (Prog1.rb) et y stocker le programme vu précédemment : print "Bonjour a tous\n" puis l’exécuter au moyen de la commande : ruby test.rb [coruscant:~/ruby] chris% ruby Prog1.rb Bonjour a tous [coruscant:~/ruby] chris% Nous venons d’entrer dans le monde de Ruby..

(5) Les débuts en programmation 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 fac.rb et exécuté. [coruscant:~/ruby] chris% ruby fac.rb 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 ./fac.rb 5 120 [coruscant:~/ruby] chris%.

(6) Explications : def fact(n) if n==0 1 else n * fact(n-1) end end n=5 print fact(n),"\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. 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). 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. La valeur de la variable n est différente de zéro, on rappelle la méthode fact afin de calculer n * (n-1) ! Le premier end termine l’instruction if. Le second end termine la fonction. Ici commence le programme proprement dit. On définit une variable qui contient la valeur de la factorielle à calculer. 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. print fact(n),"\n". 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. 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..

(7) Les commentaires 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 comm.rb Debut du programme. Commentaire sur une ligne. Commentaire sur plusieurs lignes. Fin du programme. [coruscant:~/ruby] chris%.

(8) Le programme eval.rb 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".

(9) 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 eval.rb 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 à eval.rb..

(10) Les chaînes de caractères. 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..

(11) 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>.

(12) 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 Devine.rb afin d’être soumis à l’interpréteur ruby. Le programme Devine.rb aura l’allure suivante : #Programme Devine.rb secret = rand(255) print "quel est le nombre inferieur a 256 auquel je pense? " while proposition = STDIN.gets proposition.chop! 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.

(13) Son exécution donne le résultat : [coruscant:~/ruby] chris% ruby ./Devine.rb 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. Devine.rb Fac.rb Prog1.rb eval.rb [coruscant:~/ruby] chris% ruby Devine.rb 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..

(14) ruby> c="abcdef" "abcdef" ruby> c.chop! "abcde" ruby> c "abcde" ruby> resultat=c.chop "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) print "quel est le nombre inferieur a 256 auquel je pense? " while proposition = STDIN.gets. proposition.chop! 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. Ici, on tire un nombre aléatoire dans l’intervalle 0..255 Puis on pose la question à l’usager. 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. On teste alors la valeur du nombre proposé par Rapport a celui qui a été choisi par le programme. Si les deux nombres sont égaux, c’est gagné. On le dit Et on termine le programme. Dans le cas contraire. On donnera à l’usager une indication sur la place du Nombre qu’il vient de proposer par rapport à celui Qu’il doit découvrir. Soit il est plus petit. Soit il est plus grand. Fin du if interne (if proposition.to_i > secret) Fin du if externe (if proposition.to_i == secret) Le nombre n’a pas été trouvé, on repose la question. Fin du while..

(15) Les expressions régulières 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 [] \w \W \s \S \d \D \b \b \B * + {m,n} ? | (). Signification spécification d'intervalle (par ex: [a-z] indique une lettre dans l'intervalle a à z) lettre ou chiffre; équivaut à [0-9A-Za-z] ni lettre ni chiffre caractère espace; équivaut à[ \t\n\r\f] caractère non espace chiffre; équivaut à [0-9] non-chiffre backspace (0x08) (seulement dans un intervalle) limite de mot (sauf dans un intervalle) 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 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]+/.

(16) 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>.

(17) Les listes 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 = chiffres.join("-") "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>.

(18) Les Hashes. 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..

(19) Structures de contrôle Nous allons détailler maintenant les structures de contrôle dont dispose le langage.. case 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 = STDIN.gets proposition.chop! 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 relat.rb 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..

(20) 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>. while 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>. until 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.

(21) unless 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. 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..

(22) 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". Les ruptures de boucle. 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 next redo return. pour terminer la boucle. pour terminer l’itération courante et en commencer une nouvelle. recommence l’itération courante. termine simultanément la boucle courante, et la méthode qui la contient en retournant un argument..

(23) Les itérateurs. 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 each.byte 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..

(24) 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> défini.. La commande yield (fournir) permet de passer le contrôle au bloc de code qui est associé à l’itérateur dans lequel il ,est. 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>.

(25) 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..

(26) La programmation orienté-objet. 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..

(27) Les méthodes 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 eval.rb: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é..

(28) 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..

(29) Les classes. 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> Francais=Homme.new #<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> Francais.nom 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> Homme.nom eval.rb:32: (eval):1: undefined method `nom' for Homme:Class (NameError).

(30) 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> (Homme.new).nom Tout homme a un nom nil ruby>Homme.new.nom 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..

(31) L’héritage. 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> gorille=Primate.new #<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..

(32) 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 Pigeon=Oiseau.new print "Appel de la methode Pigeon.vole:\n" Pigeon.vole 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 oiseau.rb Appel de la methode Pigeon.vole: 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 (Oiseau.new).\n" Pigeon=Oiseau.new print "Appel de la methode Pigeon.plumes:\n" Pigeon.plumes print "\nAppel de la methode Pigeon.vole:\n" Pigeon.vole print "\nCreation de l'objet Nandou (Ratites.new).\n" Nandou=Ratites.new print "Appel de la methode Nandou.plumes:\n" Nandou.plumes print "\nAppel de la methode Nandou.vole:\n" Nandou.vole.

(33) Lorsqu’on l’exécute, ce programme donne le résultat suivant : [coruscant:~/ruby] chris% ruby oiseau.rb Creation de l'objet Pigeon (Oiseau.new). Appel de la methode Pigeon.plumes: Un oiseau ca a des plumes. Appel de la methode Pigeon.vole: Un oiseau ca vole. Creation de l'objet Nandou (Ratites.new). Appel de la methode Nandou.plumes: Un oiseau ca a des plumes. Appel de la methode Nandou.vole: Desole, je ne vole pas, je cours. [coruscant:~/ruby] chris%.

(34) Redéfinition des méthodes 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 (Oiseau.new).\n" Pigeon=Oiseau.new print "Appel de la methode Pigeon.plumes:\n" Pigeon.plumes print "\nAppel de la methode Pigeon.vole:\n" Pigeon.vole print "\nCreation de l'objet Nandou (Ratites.new).\n" Nandou=Ratites.new print "Appel de la methode Nandou.plumes:\n" Nandou.plumes print "\nAppel de la methode Nandou.vole:\n" Nandou.vole Ce programme donne le résultat suivant : Creation de l'objet Pigeon (Oiseau.new). Appel de la methode Pigeon.plumes: Un oiseau ca a des plumes. Appel de la methode Pigeon.vole: Un oiseau ca vole. Creation de l'objet Nandou (Ratites.new). Appel de la methode Nandou.plumes: Un oiseau ca a des plumes. Appel de la methode Nandou.vole: 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..

(35) 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 (pigeon.vole) doit impérativement présenter une liste d’appel. class Oiseau def vole(param) print param,".\n" end def plumes print "Un oiseau ca a des plumes.\n" end end print "Creation de l'objet Pigeon (Oiseau.new).\n" Pigeon=Oiseau.new print "Appel de la methode Pigeon.plumes:\n" Pigeon.plumes print "\nAppel de la methode Pigeon.vole:\n" Pigeon.vole("Un oiseau ca vole") print "\nCreation de l'objet Nandou (Ratites.new).\n" Nandou=Oiseau.new print "Appel de la methode Nandou.plumes:\n" Nandou.plumes print "\nAppel de la methode Nandou.vole:\n" Nandou.vole("Un ratite ca ne vole pas") L’exécution de ce programme donne le résultat suivant : [coruscant:~/ruby] chris% ruby oiseau.rb Creation de l'objet Pigeon (Oiseau.new). Appel de la methode Pigeon.plumes: Un oiseau ca a des plumes. Appel de la methode Pigeon.vole: Un oiseau ca vole. Creation de l'objet Nandou (Ratites.new). Appel de la methode Nandou.plumes: Un oiseau ca a des plumes. Appel de la methode Nandou.vole: Un ratite ca ne vole pas. [coruscant:~/ruby] chris% La directive ‘super’ est aussi utilisable pour forcer le passage d’un paramètre constant à une méthode qui en réclame un. Ainsi, dans la définition d’un ratite, l’appel de la méthode vole (ratite.vole) contraint le passage de la chaîne voulue à la méthove oiseau.vole..

(36) class Oiseau def vole(param) print param,".\n" end def plumes print "Un oiseau ca a des plumes.\n" end end class Ratites<Oiseau def vole super ("Un ratite ca ne vole pas") end end print "Creation de l'objet Pigeon (Oiseau.new).\n" Pigeon=Oiseau.new print "Appel de la methode Pigeon.plumes:\n" Pigeon.plumes print "\nAppel de la methode Pigeon.vole:\n" Pigeon.vole("Un oiseau ca vole") print "\nCreation de l'objet Nandou (Ratites.new).\n" Nandou=Ratites.new print "Appel de la methode Nandou.plumes:\n" Nandou.plumes print "\nAppel de la methode Nandou.vole:\n" Nandou.vole L’exécution de ce programme donne le résultat suivant : [coruscant:~/ruby] chris% ruby oiseau.rb Creation de l'objet Pigeon (Oiseau.new). Appel de la methode Pigeon.plumes: Un oiseau ca a des plumes. Appel de la methode Pigeon.vole: Un oiseau ca vole. Creation de l'objet Nandou (Ratites.new). Appel de la methode Nandou.plumes: Un oiseau ca a des plumes. Appel de la methode Nandou.vole: Un ratite ca ne vole pas. [coruscant:~/ruby] chris%.

(37) Les contrôles d'accès Bien que ruby n'a pas de fonctions, seulement des méthodes, il y a plusieurs sortes de méthodes. Voyons ce qui se passe loraque une méthode n’est pas définie dans une classe, mais au niveau le plus élevé du programme. On peut considérer qu’une telle méthode est identique à une fonction d’un langage traditionnel. def carre(n) n*n end print carre(25),"\n" Résultat : [coruscant:~/ruby] chris% ruby carre.rb 625 [coruscant:~/ruby] chris% Dans l’absolu, il semble que cette méthode n’appartienne à aucune classe. En fait, ruby créée une classe qui est au dessus de toutes les autres et qui est nommée ‘Object’. Ainsi, tout objet peut utiliser cette méthode comme on invoque une fonction dans un langage traditionnel. def carre(n) n*n end class Calcul def puissance_6 (nombre) carre(nombre) ** 3 end end print Calcul.new.puissance_6(10),"\n" Dans l’exemple ci dessus, la méthode carre, définie au niveau ‘main’ peut sans problèmes être utilisée par la classe ‘calcul’ . L’exécution en donne la preuve. [coruscant:~/ruby] chris% ruby puiss.rb 1000000 [coruscant:~/ruby] chris% Par contre, il est interdit d'appliquer explicitement la méthode à un objet: def carre(n) n*n end print "Calcul".carre(10),"\n" Le résultat fait apparaître le message d’erreur. [coruscant:~/ruby] chris% ruby carre.rb carre.rb:5: private method `carre' called for "Calcul":String (NameError) [coruscant:~/ruby] chris%.

(38) Ainsi on préservera la nature orientée objet de ruby. Comme dans tout langage, il est possible de fournir des fonctions, Ce seront en définitive des méthodes dont le receveur implicite est self. Nous avons évoqué précédemment la séparation entre la spécification et l’implémentation, en d’autres termes quelle est la tache qui doit être accomplie et comment elle est accomplie. En toute logique, k’utilisateur de l’objet ne devrait pas avoir connaissance de son fonctionnement interne, mais seulement de ce qui entre et de ce qui en sort. Il est donc indispensable que certaines méthodes d’une classe utilisées en interne puissent être rendues invisibles à un utilisateur. Dans l'exemple trivial ci-dessous, imaginez que moteur est ce qui travaille dans la classe sans être vu. class Calcul def puiss_x(y,x) print y," puissance ",x," est egal a ",puiss(y,x),"\n" end def puiss(y,x) y ** x end private:puiss end essai=Calcul.new print "\nAppel de essai.puiss_x(3,5)\n" print essai.puiss_x(3,5),"\n" print "\nAppel de essai.puiss(3,5)\n" print essai.puiss(3,5),"\n" Le fait de déclarer la méthode puiss privée (‘private :puiss) la rend invisible à l’utilisateur de la classe. Toute référence à cette méthode génèrera ainsi une erreur. [coruscant:~/ruby] chris% ruby puiss.rb Appel de essai.puiss_x(3,5) 3 puissance 5 est egal a 243 nil Appel de essai.puiss(3,5) carre.rb:16: private method `puiss' called for #<Calcul:0x147708> (NameError) [coruscant:~/ruby] chris% Clairement, la méthode puiss n’est pas accessible directement par l’utilisateur de l’objet essai. Seule la méthode puiss_x est autorisée. (puiss).. Dans ces conditions, il est indispensable de passer par une méthode publique (puiss_x) pour utiliser la méthode privée Le concepteur de l’objet peut ainsi modifier les méthodes privées sans que l’utilisateur en ait connaissance..

(39) Les méthodes Singleton Le comportement d’une instance est entièrement définie pas sa classe, lors de la déclaration crée les méthodes qui lui sont attachées et qui seront celles de tous les objets créés à partir de cette classe. Dans certains cas, une application peut vouloir en modifier le comportement, par exemple rajouter une métgoide qui ne sera utilisée que pour un objet précis. Ce problème peut être résolu en créant une nouvelle classe, c’est ce qui est fait dans la plupart des langages, cette classe reprenant la totalité de celle précédemment créée en y ajoutant une ou plusieurs méthodes. En ruby, il est possible de redéfinir ses propres méthodes pour un objet donné rtattaché à une classe existante, on évite ainsi la création de classes spécifiques dont l’utilité n’est pas justifiée. On appellera singleton une méthode que l’on rajoute à un objet au moment de sa création à partir d’une classe existante. class Single def texte print "Texte standard de la classe Single.\n\n" end end print "Declaration de test1 (test1=Single.new).\n" test1=Single.new print "Appel de la methode texte (test1.texte).\n" test1.texte print "Declaration de test2 (test2=Single.new).\n" print "Avec modification de la methode texte.\n" test2=Single.new def test2.texte print "Texte de la classe single\nmodifie dans la declaration de test2.\n" end print "Appel de la methode texte (test2.texte).\n" test2.texte L’exécution de ce programme donne : [coruscant:~/ruby] chris% ruby single.rb Declaration de test1 (test1=Single.new). Appel de la methode texte (test1.texte). Texte standard de la classe Single. Declaration de test2 (test2=Single.new). Avec modification de la methode texte. Appel de la methode texte (test2.texte). Texte de la classe single modifie dans la declaration de test2. [coruscant:~/ruby] chris% Dans l’exemple qui vient d’être programmé, test1 et test2 appartiennent à la même classe (Single). Cependant, au moment de la déclaration de test2, la méthode texte a été redéfinie. Les deux objets ont donc des comportements différents. C’est cette méthode qui n’a été donnée qu’à un objet que l’on appelle un singleton. On utilisera abondamment ces méthodes singleton pour les éléments de l’interface graphique (Graphic User Interface) dans lesquelles on fait apparaître des boutons pour lesquels les actions à entreprendre dépendent du bouton que l’on sélectionne..

(40) Les modules Par définition, un module est semblable à une classe avec les restrictions suivantes : ➤ Il ne peut pas avoir d’instances ➤ Il ne peut pas avoir de sous classes ➤ Il est défini par les mots clé module …. end Les modules serviront dans un premier temps pour regrouper les constantes et le méthodes en rapport les unes avec les autres. Ainsi par exemple le module Math. ruby> Math.sqrt(5) 2.236067977 ruby> Math::PI 3.141592654 ruby> Math::E 2.718281828 ruby> L'opérateur :: permet d’indiquer à l'interpréteur quel est le module concerné pour trouver la valeur d'une constante. Il et aussi possible d’inclure ce module afin d’y référer directement. ruby> include Math Object ruby> sqrt(9) 3.0 ruby> PI 3.141592654 ruby> E 2.718281828 ruby> Certains langages comme C++ permettent un héritage multiple, c’est à dire d’avoir plusieurs super-classes directes pour une classe donnée. Prenons comme exemple un radio réveil à cassettes. Cet objet est simultanément un objet de la classe des indicateurs de temps, des postes de radio et des lecteurs de cassettes. Il doit donc hériter des caractéristiques de chacune de ces différentes classes. Ruby n’offre pas véritablement l’héritage multiple, par contre, il propose une technique différente comme une alternative a cette propriété. Cette technique est le ‘mixin’. Nous avons fait remarquer comme préalable qu’un module ne peut ni être instancié, ni être sous-classé. Toutefois, si un module est inclus dans la définition d’une classe, l’ensemble de ses méthodes seront, par définition, mélangées à celles de la classe. On dispose ainsi d’une possibilité d’obtenir certaines propriétés. Soit une classe qui posséde une méthode ‘each’ le fait d’y mêler par ‘include’ le mode ‘Enumerable’ de la librairie standard permet d’accéder gratuitement aux méthodes ‘sort’ et ‘find’. Il est ainsi possible de définir les fonctionnalités de base de l’héritage multiple tout en représentant par un arbre simple les relations de classe..

(41) Les objets procédures Afin de faire face à des évènements inattendus, il est parfois indispensable de traiter des lignes de code comme si il s’agissait de lignes de données. Pour cela, on pourra créer un nouvel objet, l’objet procédure). ruby> bonjour = proc { ruby| print "Bonjour la compagnie\n" ruby| } #<Proc:0x146c54> ruby> L’objet ainsi créé auquel se réfère ‘bonjour’ peut s’exécuter via sa méthode ‘call’ ruby> bonjour.call Bonjour la compagnie nil ruby> Mais, il peut tout aussi bien être utilisé comme argument d’une méthode. bonjour = proc { print "Bonjour la compagnie\n" } def executer (param) print "On va executer la procedure passee en parametre...\n\n" param.call print "\nPrecedure executee...\n" end executer (bonjour) Résultat : [coruscant:~/ruby] chris% ruby proc.rb On va executer la procedure passee en parametre... Bonjour la compagnie Precedure executee... [coruscant:~/ruby] chris% Il existe par ailleurs une méthode ‘trap’ qui nous permet de récupérer certains les évènements du système. Par exemple le fait de taper Ctrl-C permet de sortir de l’interpréteur. evenement = proc{ print " <- Ne tentez pas de tuer le programme SVP.\n" } trap "SIGINT", evenement while proposition = STDIN.gets proposition.chop! if proposition.to_i == 0 print "On finit le programme\n" break end end.

(42) Ce programme lit des nombres sur l’entrée standard et se termine normalement si on donne la valeur zéro. On veut éviter que l’usager termine brutalement son programme au moyen de la commande Ctrl-C. Pour ce faire, on rajoute la commande trap qui permet dans ce cas d’exécuter la procédure ‘evenement’. L’exécution de ce programme donne donc : [coruscant:~/ruby] chris% ruby event.rb 1 2 ^C <- Ne tentez pas de tuer le programme SVP. 3 0 On finit le programme [coruscant:~/ruby] chris% Ceci étant, il est toujours possible si une urgence se produit de tuer le programme au moyen de la commande Ctrl-D. Il n’est pas indispensable de nommer une procédure trap. L’utilisation d’une procédure annonyme est toujours possible. On aurait ainsi pu écrire : trap "SIGINT", proc{ print " <- Ne tentez pas de tuer le programme SVP.\n"} ou encore plus simplement : trap "SIGINT", 'print " <- Ne tentez pas de tuer le programme SVP.\n"'.

(43) Les variables Il existe deux grandes familles de langage de programmation : Les langages déclaratifs dans lesquels toute variable doit être déclarée. Ceci permet de spécifier son type, si elle est ou non modifiable (variable ou constante) et sa visibilité. Les langages pour lesquels aucune déclaration n’est nécessaire. Une variable étant automatiquement déclarée dés que son nom apparaît. C’est le cas de ruby. Le premier caractère du nom de la variable permet de la caractériser. Premier caractère du nom $ @ [a-z] _ [A-Z]. Type variable globale variable d'instance variable locale variable locale constante. Il n’existe que deux exeptions à cette règle, ce sont les deux pseudo-variables : ‘self’ qui désigne l’objet en cours d’exécution. ‘nil’ qui est la valeur sans signification. D’après les règles qui ont été données ci dessus, les noms devraient représenter des variables locales, mais en fait ‘self’ est une variable globale qui est maintenue à la bonne valeur par l’interpréteur et nil est une constante, elle sera d’ailleurs affectée à toute variable non initialisée. Il est bien entendu impossible d’affecter une quelconque valeur a ces deux variables. ruby> self main ruby> self=proc eval.rb:32: (eval):1: compile error (SyntaxError) (eval):1: Can't change the value of self self=proc ^ [coruscant:~/ruby] chris%.

(44) Variables globales Toute variable sera considérée comme globale si son nom commence par $. Elle pourra être utilisée n'importe où dans le programme. Avant initialisation, elle sera automatiquement initialisée à nil. ruby> $valeur nil ruby> $valeur=154 154 ruby> $valeur 154 ruby> $valeur+=1 155 ruby> Il faut se méfier des variables globales. Leur accessibilité les rend modifiables depuis n’importe quelle partie du programme et leur abus peur poser des problèmes lors de la mise au point. Un programme bien analysé limite leur nombre au strict minimum. Toutefois, une particularité agréable de ce type de variable est la possibilité qui est offerte de les tracer, c’est à dire d’invoquer une procédure chaque fois que leur valeur est modifiée. trace_var :$x, proc{print "$x vient d'etre modifiee, elle vaut maintenant ", $x, "\n"} for $x in (1..5) end [coruscant:~/ruby] chris% ruby trace.rb $x vient d'etre modifiee, elle vaut maintenant 1 $x vient d'etre modifiee, elle vaut maintenant 2 $x vient d'etre modifiee, elle vaut maintenant 3 $x vient d'etre modifiee, elle vaut maintenant 4 $x vient d'etre modifiee, elle vaut maintenant 5 [coruscant:~/ruby] chris% Ainsi, une variable globale peut être utilisée comme un déclencheur. Chacune de ses modification lancera une procédure par exemple la mise à jour d’une interface graphique.Elle sera alors appelée une ‘variable active’. Il existe comme en perl une série de variables globales prédéfionies. Les principales sont : Variable $! $@ $_ $. $& $~ $n $= $/ $\ $0 $* $$ $?. Contenu Dernier message d'erreur. Emplacement de l'erreur. Dernière chaîne lue par gets (variable standard d’entrée). Numéro de la dernière ligne lue par l'interpréteur. Dernière chaîne trouvée dans une expression régulière. Dernière trouvaille de type tableau dans une expression régulière. La nième trouvaille de la dernière expression régulière (équivaut à $~[n]) Sensibilité (minuscules/majuscules) des recherches par expression régulière. Séparateur des éléments en entrée. Séparateur des éléments en sortie. Le nom du script courant ruby. La liste des arguments de la ligne de commande. L'identifiant du process de l'interpréteur (PID). Code retour du dernier processus fils.. A noter au passage que $_ et $~ bien que portant un nom qui suggère une portée globale sont en fait des variables locales..

(45) Variables d'instance Une variable sera une variable d’instance si son nom commence par @. Sa portée se limite aux objets auxquels elle se réfère (self). Deux objets différents appartenant à une même classe peuvent avoir des valeurs différentes dans leurs variables d’instances dans leurs variables d’instance respectives. De l’extérieur de l’objet, les variables ne peuvent pas être altérées, elles ne sont même pas visibles. En fait, les variables d’instance ne sont jamais publiques. La seule possibilité d’y accéder seront les méthodes explicitement fournies par le programmeur. Comme les variables globales, jusqu’à leur première affectation elles auront la valeur nil. Les variables d’instance ne sont pas déclarées. En fait, chacune d’elle est dynamiquement ajoutée à l’oblet au moment de sa première invocation. class Instance def set_1(n) @inst1 = n print "Dans Instance : @inst1 = ",@inst1,", inst2 = ",@inst2,"\n" end def set_2(n) @inst2 = n print "Dans Instance : @inst1 = ",@inst1,", inst2 = ",@inst2,"\n" end end i = Instance.new i.set_1(2) i.set_2(4) print "Dans Main : @inst1 = ",@inst1,", inst2 = ",@inst2,"\n" Résultat du programme : [coruscant:~/ruby] chris% ruby inst.rb Dans Instance : @inst1 = 2, inst2 = nil Dans Instance : @inst1 = 2, inst2 = 4 Dans Main : @inst1 = nil, inst2 = nil [coruscant:~/ruby] chris%.

Références

Documents relatifs

L’étude du bilan scolaire selon le type de projet d’établissement fait apparaître que les élèves ont plus de réussite, sans redoublement, dans le groupe des projets

En réalité mon étude semble indiquer que, même dans les cas de publications considérées comme inacceptables par les participants, ces derniers ont eu plus tendance à

L’archive ouverte pluridisciplinaire HAL, est destinée au dépôt et à la diffusion de documents scientifiques de niveau recherche, publiés ou non, émanant des

Les prises de vues aériennes et les couvertures topogra phiques récentes montrent en effet une différence légère mais sensible (supérieure à 1°) dans les

Trois données expérimentales convergentes nous suggèrent qu’UCP1 pourrait participer à la fonction contractile de ces cellules : (1) le contenu en UCP1 de l’utérus est augmenté

It has shown that a very large number of join and leave events are globally necessary before the first topological change occurs in those overlay networks.. As future work we intend

O presente trabalho teve como objetivo maior o levantamento fitossociológico das principais plantas daninhas presentes nas lavouras de mamão nos municípios de Linhares e Sooretama,

For this purpose, we think to improve the precision of the elderly location by using the genetic algorithm in the anchors selection phase in order to increase the