Syntaxe et originalités du langage
Ruby est un langage de script orienté objet, inventé en 1993 au Japon par Yukihiro Matsumoto, surnommé matz.
Dans ce langage, tout ce qui est manipulable par l’utilisateur est un objet, et sa syntaxe se montre particulièrement agréable à utiliser dans cette optique, la programmation orientée objet. Ruby est en outre multiplatformes. Il a également l’avantage d’être fourni avec des outils très appréciés des développeurs comme le debogueur, le ’’profiler’’ et le ’’tracer’’. La bibliothèque standard de Ruby est très fournie : ’’threads’’, ’’bignum’’, matrices, réseaux, cgi, xml … vous y trouverez certainement de quoi répondre à vos besoins.
Ruby dispose d’ailleurs de beaucoup d’autres bibliothèques toutes aussi utiles que celles offertes par la ’’Standard Lib’’. Par exemple, Qt, Gtk et FOX sont disponibles pour créer des interfaces graphiques, et on trouve aussi des parsers xslt, un mod_ruby afin développer des pages web dynamiques, etc.
Un premier programme
Notre premier exemple et programme consistera à effectuer le calcul d’une factorielle mathématique. Par convention, les fichiers Ruby portent l’extension rb. Pour éxécuter un programme en Ruby, il suffit de taper la commande :
ruby nom_du_fichier.rb
Sous Windows, l’installeur de Ruby fait en sorte qu’il suffit de cliquer sur un fichier ruby afin de l’éxécuter.
# Une fonction factorielle
def factorielle(n)
if n == 0
return 1
else
return n * factorielle(n - 1)
end
end
n = 1234
puts "La factorielle de " + n.to_s + " est " + factorielle( n ).to_s
Vous ne l’avez sûrement pas remarqué, mais Ruby fait une conversion implicite du type entier à Bignum et de Bignum à entier lorsque c’est nécessaire. Ainsi le travail du programmeur est grandement facilité et l’utilisation de la mémoire optimisée.
Comme on peut le constater, les opérateurs pour effectuer des opérations arithmétiques sont identiques à ceux du C ou d’autres langages classiques. On peut déjà faire quelques remarques à propos de la syntaxe de Ruby. Les parenthèses dans appels de « fonctions » (le terme adéquat est méthode) sont facultatives, tout comme les points virgules à la fin d’une ligne, pour marquer la fin d’une instruction. Par contre, ces derniers sont obligatoires lorsque l’on veut écrire plusieurs instructions sur une seule ligne.
Les variables
En Ruby, il existe cinq catégories de variables :- variables locales ;
- variables globales ;
- variables d’instances ;
- variables de classes ;
- les constantes (bien que ce soit usuellement un contre-sens d’appeller variable une constante, Ruby propose ici un comportement original).
Plusieurs d’entre-elles sont liées au modèle objet de Ruby. Si la programmation orientée objet est une chose nouvelle pour vous, ne vous attardez pas sur ces détails pour le moment, ils seront expliqués au moment opportun.
Les variables locales débutent par un caractère minuscule ou de soulignement _. Évidemment, elles ne sont accessibles que dans la portée où elles ont été déclarées. En Ruby, il s’agit souvent d’une méthode ou d’un bloc de code.
Les variables globales ont comme premier caractère le sigle dollar $. Elles sont accessibles partout.
Les variables d’instances sont propres à chaque objet et commencent par le caractère arobase ou ’’at’’ @. On les dénomme parfois attributs car elles sont propres à un objet en particulier.
Les variables de classes débutent par un double arobase @@.
Enfin, en Ruby, les constantes ne sont pas tout à fait des constantes. Lorsqu’on essaye de leur affecter une nouvelle valeur après leur initialisation, un message d’avertissement est émis (un ’’warning’‘) et la constante est modifiée. Cette sorte de « variable protégée » débute par une majuscule.
Toutes ces variables, exceptées les variables de classes, sont implicitement déclarées comme nil par défaut.
En résumé :
variable_locale
<pre><code>$variable_globale
@variable_d_instance
@@variable_de_classe
Constante</code></pre>
En plus des traditionelles variables, Ruby dispose également de « pseudos variables. » Il n’est pas possible d’affecter une valeur à des pseudos variables.
La pseudo variable self représente l’objet courant et est ainsi équivalent au ’’this’’ du C++. Les variables true, false et nil sont respectivement vrai, faux et indéfini. Cette valeur nil est en fait équivalente au ’’NULL’’ trouvé en SQL par exemple ; un point intéressant est que ’’nil’’ est tout de même évalué comme ’’false’’ dans les structures conditionnelles. Les deux autres pseudos variables que sont FILE et LINE représentent respectivement le nom et la ligne du fichier courant.
Les opérateurs
Voici la liste opérateurs Ruby par ordre de priorité décroissant : ::
<pre><code>[]
**
+ (unaire) - (unaire) ! ~
* / %
+ -
<< >>
&
| ^
> >= < <=
<=> - -= != =~!~
&&
||
.. ...
? :
= += -= *= /= %= **= <<= >>= &= |= ^= &&= ||=
not
and or</code></pre>
En Ruby, nombre d’opérateurs sont en réalité des appels de méthodes. En effet, l’écriture a + b est en réalité interprété comme a., soit, l’appel d’une méthode + sur la variable a, avec le paramètre b. Ceci permet de surcharger, ie. modifier, très facilement les opérateurs avec une syntaxe bien plus simple qu’en C+. Par contre, certains opérateurs ne sont pas des appels de méthodes et donc ne peuvent pas être surchargés :
...
<pre><code>!
not
&&
and
||
or
::
!=
= += -= *= /= %= **= <<= >>= &= |= ^= &&= ||=
?</code></pre>
Il existe un dernier opérateur qui permet d’obtenir des informations à propos d’une expression : defined?. S’il le peut, defined? renvoie une chaîne de caractères pour décrire l’expression. Dans le cas contraire, nil est renvoyé.
defined? var # nil
<pre><code>var = 1
defined? var # "local-variable"
defined? $stdout # "global-variable"
defined? print # "method"</code></pre>
Les mots clefs
Les mots réservés ou mots clefs en Ruby ne peuvent être utilisés ni par des variables, ni par des constantes. Ils peuvent être utilisés pour les noms de méthode, à condition qu’un récepteur soit précisé. Voici la liste des mots réservés en Ruby :
BEGIN
<pre><code>do
next
then
END
else
nil
TRUE
alias
elseif
not
undef
and
end
or
unless
begin
ensure
redo
until
break
FALSE
rescue
when
case
for
retry
while
class
if
return
yield
def
in
self
__FILE__
defined?
module
super
__LINE__</code></pre>
Les structures de contrôles
Ruby dispose des structures de contrôles classiques trouvées dans les autres langages, sans pour autant reprendre la syntaxe du C. En effet, les parenthèses d’une condition sont facultatives et le blocs sont délimités par ’’end’’. Le code suivant utilise les structures de contrôles suivantes : ’’if’’, ’’while’’, ’’unless’’, ’’until’’, ’’case’’ et ’’for’’. Comme le montre le code, on peut utiliser les mots clefs ’’and’’ et ’’or’’ à l’intérieur des conditions. Les opérateurs de comparaisons dont identiques à ceux du C.
#! /usr/bin/ruby
str = "Ruby" # une chaîne de caractère
var = 5 # un entier
i = 0
lettre = "b";
tableau = [ 6, 7, 9, 2, 4, 9, 1 ]
j = 0
# La condition if
if var - 5 and str - "Ruby"
print str + " powered !" + "\n"
else
print "Python is good too" + "\n"
end
# La boucle while
while i < 10
if ( i+= 1 ) - 3
next # Revient juste avant la condition
end
print "i = " + i.to_s + "\n"
end
# L'instruction unless équivalente au if inversé
unless i > 100 then
print "i < 100 \n"
else
print "i > 100 \n"
end
# La boucle until équivalente au while inversé
until i > 20
print "i = " + i.to_s + "\n"
i += 1
if i - 17
break
end
end
# Le case equivalent au switch
case lettre
when "a":
print "a \n"
when "b":
print "b \n"
when "c":
print "c \n"
when "d":
print "d \n"
else
print "other \n"
end
# Un autre style de boucle
loop = 5
0.upto( loop ) { |j|
puts j
}
# La valeur de loop n'est pas modifiée
loop.downto( 0 ) { |j|
puts loop
}
# Le for, pour la manipulation des tableaux
for t in tableau
print "tableau[ " + j.to_s + " ] = " + t.to_s + "\n"
j += 1
end
On peut remarquer qu’il est possible d’effectuer des boucles sans utiliser de structure de contrôle. En effet, les méthodes ’’upto’’ et ’’downto’’ de la classe ’’Integer’’ répètent un bloc de code jusqu’à ce que la valeur passée en argument soit atteinte. Ce bloc de code doit être délimité par des accolades ({ ... }). Ces deux méthodes ont notamment l’avantage d’éviter certaines boucles infinies.
10.upto( 1 ) { |i| puts i } # Affichera seulement 10
La boucle ’’for’’ en Ruby est semblable à celle du ’’foreach’’ en PHP. Elle permet d’obtenir chaque élément d’un tableau sans se soucier de sa longueur et d’incrémenter un compteur interne. La variable tableau indique sur quel ’’tableau’’ la boucle va travailler et ’’element’’ représente l’élément du tableau actuellement sélectionné. Cette dernière structure de contrôle nous amène à la manipulation des tableaux en Ruby.
Les tableaux
Les tableaux permettent de stocker des objets de n’importe quel type et sont représentés par la classe ’’Array’’. Cette classe fournie environ soixante méthodes pour faciliter leur manipulation. Les opérateurs ont été surchargés afin de travailler plus facilement avec les tableaux.
’’La documentation de Ruby’‘
’’La liste complète de toutes les méthodes de la classe’’ Array ’’est consultable avec la documentation de référence de Ruby : http://www.ruby-doc.org ’’
tableau = [1, 2, 3, "a", "b", "c"]
tableau_imbrique = [ 1, 2, 3, [ "a", "b", "c" ] ]
print "Tableau après initialisation:\n"
print tableau.join( " " ) + "\n"
print "\nPremière occurence de l'élément 2:\n"
print "tableau[ " + tableau.index( 2 ).to_s + " ] = 2\n"
print "\nOpérations pop et push sur le tableau:\n"
tableau.pop
tableau.pop
tableau.pop
tableau.push( 2 )
tableau.push( 9 )
tableau.push( 7 )
tableau.push( 5 )
tableau.push( 6 )
tableau.push( 4 )
tableau.push( nil )
print tableau.join( " " ) + "\n"
print "\nInverse l'ordre des éléments du tableau:\n"
tableau = tableau.reverse
print tableau.join( " " ) + "\n"
print "\nSuprime tous les éléments nil du tableau:\n"
tableau = tableau.compact
print tableau.join( " " ) + "\n"
print "\nTrie par ordre croissant les éléments d'un tableau:\n"
tableau = tableau.sort
print tableau.join( " " ) + "\n"
print "\nSuprime les doublons du tableau:\n"
tableau = tableau.uniq
print tableau.join( " " ) + "\n"
print "\nSuprime l'élément à la 2eme position et l'élément 3 du tableau:\n"
tableau.delete_at( 1 )
tableau.delete( 3 )
print tableau.join( " " ) + "\n"
print "\nSuprime tous les éléments pairs du tableau:\n"
tableau.delete_if{ |e| e % 2 - 0 }
print tableau.join( " " ) + "\n"
print "\nRempli les éléments d'un tableau sur une plage:\n"
tableau.fill( "fill", 2, 4 )
print tableau.join( " " ) + "\n"
print "\nEfface le contenu du tableau:\n"
tableau.clear
print "\nLongueur du tableau: " + tableau.length.to_s + "\n"
La plupart de ces fonctions restent classiques. Par contre il y a une syntaxe particulière pour ’’delete_if’’.
tableau.delete_if{ |e| e % 2 - 0 }
En fait, cet appel de fonction est semblable au ’’for’’. La variable ’’e’’ va prendre chaque valeur du tableau, puis entre les deux accolades on peut effectuer n’importe quelles instructions (on n’est pas limité à une seule comme l’est l’exemple). On appelle cela un « bloc de code » et c’est une particularité très puissante et très utilisée en Ruby.
Les fonctions
Une fonction ? Mais il semble y avoir une contradiction ! En effet nous avons vu que tout est objet en Ruby. Or, une fonction n’appartient à aucune classe. En fait, si, ce qu’on appelera fonction en Ruby appartient bel et bien à un objet et se trouve donc être une méthode, rattachée implicitement par Ruby à la classe ’’Objetc’’.
Pour définir une méthode ou une fonction, ce qui est la même chose, il faut utiliser le mot clef ’’def’’ et terminer la définition avec ’’end’’. En plus de permettre l’utilisation des arguments par défaut, Ruby autorise également les listes variables d’arguments, de passer un tableau à la place de plusieurs variables, de passer des blocs d’instructions et des arguments sous forme d’une table ’’hash’’. Pour comprendre cela, analysons le code source suivant :
# Argument par défaut
def arg_defaut( arg1, arg2 = 4 )
print "Argument par défaut: "+ arg2.to_s + "\n"
end
# Tableau à la place d'arguments
def arg_tableau( a, b, c, d )
print "Passe un tableau à la place des arguments : "
print a.to_s + " "
print b.to_s + " "
print c.to_s + " "
print d.to_s + "\n"
end
# Passage d'arguments de type hash
def arg_hash( nom, param )
print nom + ": " + param["clef 1" ] + " " + param[ "clef 2" ] + "\n"
end
# Liste variable d'arguments
def liste_variable( a, *b )
print "Liste variable d'arguments: " + b.join( ", " ) + "\n"
end
# Utilisation d'un block de code, ou Proc
def bloc1( var, &block )
print "Utilisation d'un bloc: "
for v in var
block.call( v )
end
print "\n"
end
# Utilisation d'un block de code avec yield
def bloc2( b )
print "Autre utilisation d'un bloc : "
if block_given?
yield( b )
else
b
end
end
arr = [ "a", "b", "c", "d" ]
arg_defaut( "Ruby c'est bien !" )
arg_tableau( *arr )
arg_tableau( "z", *arr[ 1, 3 ] )
arg_tableau( *( 1 .. 4 ) )
arg_hash( "Hash", 'clef 1' => "valeur 1", 'clef 2' => "valeur 2" )
liste_variable( 1, 2, 3, 4, 5 )
bloc1( arr ) { |l| print l + " " }
bloc2( "Les blocs en ruby" ) { |s| print s.length.to_s + "\n" }
bloc2( "c'est vraiment bien !" ) do |s| print s.length.to_s + "\n" end
La définition d’un argument par défaut se fait à l’aide d’une affectation, comme en C++. Par contre, pour définir une liste variable d’arguments, ce n’est franchement pas aussi compliqué qu’en C puisque tout en mis dans un objet unique de classe tableau, quand la méthode reçoit les arguments. Ce tableau doit être d’éclaré avec une astérisque comme préfixe.
Pour ce qui est des blocs de code, on peut les délimiter soit par des accolades, soit par un do … end. Ensuite la fonction récupère un objet de type ’’Proc’’, qui représente ce bloc de code autonome. Pour l’éxécuter, il suffit d’appeller la méthode ’’call’’. Pour savoir si un bloc de code a été passé à la fonction, il faut utiliser la fonction ’’block_given?’’. La Standard Lib de Ruby en fait un usage très fréquent.
Si aucun ’’return’’ n’est précisé dans une méthode pour expliciter quelle valeur doit être renvoyée au final, Ruby retournera par défaut la dernière variable utilisée ou affectée.
<< Sommaire | L’orientation objet de Ruby >>


