Programmation Ruby/Syntaxe

Un livre de Wikibooks.

<< Retour au sommaire

Sections

[modifier] Syntaxe du langage

La syntaxe de ruby est à la fois simple, car elle permet de lire simplement le code source, et complexe, car à la manière du perl il y a plusieurs manières d'écrire une même instruction.

[modifier] Nomenclatures

Notons que par nomenclature les méthodes terminant par un point d'exclamation ! sont non pures : elles modifient l'objet. Les méthodes terminant par un point d'interrogation ? renvoient un booléen (vrai ou faux)

[modifier] Identifiants

Tout nom (que cela soit pour les variables, méthodes, classes...) doit respecter une certaine nomenclature : tout identifiant doit commencer soit par une lettre, soit par un souligné (_), et bien sur ne doit pas être un des mots réservés du langage.

Exemples :

MaVariables    => Ok
_maVariable    => Ok
3Variables     => Erreur

[modifier] Commentaires

En Ruby, les commentaires peuvent prendre deux formes. La plus commune insère un commentaire sur une seule ligne et débute par le caractère dièse ("#"). Le reste de la ligne est alors considéré comme un commentaire.

#Ceci est un commentaire
 
# ceci est un
# bloc de
# commentaire
 
puts "toto"  # ce commentaire suit une instruction

Une seconde forme permettant d'insérer des commentaires sur plusieurs lignes est plutôt réservée à l'écriture de documentation. Il s'agit de délimiter les lignes de commentaires par une ligne "=begin" et une ligne "=end".

=begin
voici un commentaire
sur plusieurs lignes
utilisant la seconde forme
=end

[modifier] Typage de canard

Si vous avez déjà utilisé des langages typés tels que C ou Java, vous êtes familier de la notion de "type". Ces langages attribuent à chaque variable un "type" c'est à dire un ensemble de choses qu'elle est capable de faire. Lorsqu'une variable a un type donné, le langage considère qu'on ne peut pas demander autre chose à celle-ci que ce que son type l'y autorise. Ainsi les langages typés détectent une erreur de programmation dans un programme avant même de l'utiliser en vérifiant simplement que ce que l'on demande à chaque variable est bien à la portée de son type.

Ruby est fondé sur l'approche inverse, il fait l'hypothèse qu'à priori, on peut demander n'importe quoi à une variable et qu'il détectera une erreur seulement lorsqu'une variable ne sera pas en mesure de faire ce qu'on lui demande au moment où on lui demande. C'est en celà que réside tout l'intérêt de ce langage: il autorise les variables à acquérir ou perdre des fonctionnalités au cours de leur existence. Pour cette raison, ceux habitués au langages typés seront surpris de constater que les paramètres des méthodes n'ont pas de type en Ruby: cette notion n'existe simplement pas.

Ce mécanisme est parfois nommé "typage de canard" (Duck Typing) et résumé ainsi: "si ça a des plumes et que ça fait 'coincoin' alors c'est sûrement un canard". Illustrons cela avec un exemple:

# le canard, si on le lui demande
# gentiment, sait faire coincoin
class Canard
 def faire_coincoin
  puts "coincoin"
 end
end
 
# l'humain parle (trop)
class Humain
 def parle
  puts "bla bla"
 end
end
 
# un canard:
canard = Canard.new
# un humain
humain = Humain.new
# un imitateur de canard !
imitateur = Humain.new
# la puissance de Ruby
def imitateur.faire_coincoin
 puts "coin, coin !"
end
 
# et maintenant voici le typage de canard:
canard.faire_coincoin # => "coincoin"
imitateur.faire_coincoin # => "coin, coin!"
humain.faire_coincoin # provoque une erreur

Comme vous pouvez le constater, même si les humains ne font pas coincoin d'après la classe Humain, certains peuvent apprendre et Ruby laissera faire les imitateurs de canards.

Ce mécanisme, permet d'enrichir certains objets comme nous venons de le voir, mais il permet surtout d'écrire du code générique (réutilisable) sans trop effort:

def une_fonction(parametre)
 parametre << "toto"
end
 
# utilisons une_fonction avec différents paramêtres
# définissant chacun l'opérateur "<<"
a = [1,2,3] # un tableau
une_fonction(a) # => [1,2,3,"toto"]
s = "une bonne blague de " # une chaine de caractères
une_fonction(s) # => "une bonne blague de toto"
f = File.new("fichier","w") # un fichier
une_fonction(f) # ajoute "toto" à la fin du fichier

[modifier] Portée et syntaxe des variables

La portée des différentes variables est définie par leur syntaxe :


[modifier] Variable locale

Une variable locale doit être nommée avec pour premier caractère soit en minuscule, soit un underscore.

exemple :

maVariable
_variable
i4

La portée d'une variable locale est le bloc courant, sauf si elle est définie en dehors du bloc :

variable = "En dehors du bloc"
puts variable                  #     => En dehors du bloc
begin 
  variable = "Dans le bloc"
  puts variable                #     => Dans le bloc
end
puts variable                  #     => Dans le bloc

[modifier] Variable globale

Une variable globale doit être préfixée avec dollar ($) comme premier caractère :

exemple:

$maVariableGlobale
$VARIABLE
$_VAR

Comme son nom l'indique, une variable globale est accessible dans tout le programme :

$maGlobale = 3 
puts maGlobale              #        => 3
 
class A
 $maGlobale = 8
 
 def initialize
   puts $maGlobale
 end
end
 
A.new                       #       => 8
puts $maGlobale             #       => 8

[modifier] Variable d'instance

Une variable d'instance est une variable qui n'est accessible qu'après l'instanciation d'un objet. La variable sera alors accessible en utilisant cette syntaxe : <Nom de l'instance>.<Nom de la variable>. Son nom doit être préfixé avec le caractère arobase (@)

Exemple :

class A
 attr_reader :variableInstance
 def initialize
   @variableInstance = 42
  end
 
 def to_s
   return @variableInstance.to_s #Correspond a self.variableInstance.to_s
 end
end
 
puts variableInstance       #      => Erreur
puts A::variableInstance    #      => Erreur
test=A.new
puts test.variableInstance  #      => 42
puts variableInstance.to_s  #      => 42

[modifier] Variable de classe

Une variable de classe est commune à toutes les instances d'une même classe. Son nom est préfixé par deux arobases (@@).

Exemple :

class A
 @@variabeDeClasse = 0
 
 def initialize
   @@variableDeClasse += 1
   @variableDeClasse = 5  #Ceci n'est pas une variable de classe, c'est juste pour l'exemple
 end
 
 def nombreInstance
   return @@variableDeClasse
 end
 
 def variableDeClasse
   return @variableDeClasse
 end
end
 
test1 = A.new
puts test1.nombreInstance    #   => 1
puts test1.variableDeClasse  #   => 5
test2 = A.new
puts test1.nombreInstance    #   => 2
puts test1.variableDeClasse  #   => 5

[modifier] Constante

Une constante définit un élément qui ne pourra jamais changer au cours de l'éxécution du programme. Celle-ci doit obligatoirement être définie lors de sa déclaration. Une constante se nomme uniquement avec des caractères majuscules (et des chiffres ou '_' ) :

MACONSTANTE = 42
puts MACONSTANTE      #       => 42
MACONSTANTE = 18      #       => Erreur 
 
POIDS_747 = 1234
POIDS_747 = 0     # => Erreur

[modifier] Référence

les symboles ':' s'apparenterait plus a des enum en C et non pas des pointeurs.

les references sont utilisées a chaque fois q'une variable fait référence à une autre variable (pas très clair), exemple:

a = "toto"
a.object_id # => 143
b = a
b.object_id # => 143
 
a[0] = "l"
b # => "loto"
À faire

Vérifier le paragraphe suivant

Une référence correspond à l'adresse d'une variable, "comparable" aux pointeurs en C ou encore aux références en C++. La portée d'une référence est identique à celle d'une variable locale. Elles peuvent réferencer tout types de données (variable, méthode, expression...). Les références sont préfixés avec le caractère deux points (:)

class A
 attr_reader :variable #attr_reader utilise des références
 
 def initialize
   @variable = 18
 end
end
 
A.new
A.variable            #        => 18

Ici attr_reader (qui permet de créer une méthode d'accès pour chaque variable dont la référence est donnée) prend des références comme paramètre.

Les références de même nom sont UNIQUE en mémoire, et ne sont initialisées qu'une seule fois :

ma_table_de_hash1 = { "langage" => "ruby"}
ma_table_de_hash2 = { "langage" => "un autre langage"}
 
ma_table_de_hash1["langage"]            #  => ''ruby''
ma_table_de_hash2["langage"]            #  => ''un autre langage''
 
ma_table_de_hash1["langage"].object_id  #  => ''23530''
ma_table_de_hash2["langage"].object_id  #  => ''23560''

On voit ici clairement que chaque clé du tableau associatif est une chaîne de caractères unique (object_id différent). Le désavantage de cette méthode est qu'il est coûteux en temps et en mémoire. Nous allons, donc pour économiser quelques ressources, utiliser des symboles:

ma_table_de_hash1 = { :langage => "ruby"}
ma_table_de_hash2 = { :langage => "un autre langage"}
 
ma_table_de_hash1[:langage]            #  => ''ruby''
ma_table_de_hash2[:langage]            #  => ''un autre langage''
 
ma_table_de_hash1[:langage].object_id  #  => ''24260''
ma_table_de_hash2[:langage].object_id  #  => ''24381''

On voit ici clairement que les références correspondent au même objet (même object_id). De même nous pouvons utiliser les références pour avoir des paramètres nommés :

def fonction_test param #param est en réalité un tableau assossiatif
  puts param[:text]
  puts param[:var]
end
 
fonction_test :var => "world", :text => "Hello"
# => Hello
#    world

NB: Les prochaines versions de ruby auront un système plus propre pour utiliser les paramètres nommés

Il est toujours intéressant d'utiliser les symboles lorsque vous avez besoin d'un simple identifiant.

[modifier] Expressions

En ruby une expression correspond à tout ce que peut renvoyer un objet, soit à peu près tout :

42                           => 42
2 + 2                        => 4

[modifier] Parenthésage

Le parenthésage permet de spécifier des priorités lors de l'interprétation. Comme pour une formule mathématique, les expressions entre parenthèses sont évaluées en premier :

3*2+4                       => 10
3*(2+4)                     => 18      # Ruby évalue d'abord l'expression entre parenthèses
3*(2*(2+4))                 => 36      # On peut incrémenter le niveau de parenthésage

[modifier] Assignation

L'assignation d'un objet à une variable se fait avec le charactère égal (=). La variable doit être l'élément de gauche, l'objet ou l'expression doit se trouver à droite :

a = 42           # assigne un objet de type Fixnum et ayant pour valeur 42 à a
a = 40 + 2       # idem

De même grace à l'objet Proc il est possible d'affecter à une variable un bloc de code :

a = Proc.new do
    |value|
       2+value
    end
a.call(40)          #      => 42

Nous étudierons par la suite plus en détail l'objet Proc.

[modifier] Assignations parallèles

Ruby permet d'assigner plusieurs variables à la fois, en séparant celles-ci par une virgule (,). Par exemple pour intervertir deux variables :

a = 8
b = "test"
 
a, b = b, a
a              #    => "test"
b              #    => 8

De même nous pouvons affecter les valeurs d'un tableau à plusieurs variables (nous verrons l'utilisation des tableaux plus tard).

a = [1, "test", 42]
a                          #  => [1, "test", 42]
 
a,b = [1, "test", 42]
a                          #  => 1
b                          #  => "test"
 
a,b,c,d = [1, "test", 42]
a                          #  => 1
b                          #  => "test"
c                          #  => 42
d                          #  => nil

[modifier] Appels système

Les appels système peuvent se faire de différentes manières

[modifier] Quotes inversées

Appel d'une commande et récupération de la sortie dans un tableau

system_name = `uname`     # => "Linux\n"
system_name = `uname -a`  # => "Linux machine 2.6.22.5 #2 SMP Fri Aug 25 14:31:07 CEST 2006 i686 unknown\n"

Pour passer des variables, il faut absolument entourer la variable de #{} par exemple avec :

arg = "-a"    #         => "-a"

si l'on se contente de mettre #a

system_name = `uname #arg`  #  => "" l'argument #a est passé et non la valeur de a, ce qui retourne l'erreur :
# Try `uname --help' for more information.

Par contre :

system_name = `uname #{arg}` => "Linux machine 2.6.22.5 #2 SMP Fri Aug 25 14:31:07 CEST 2006 i686 unknown\n"

[modifier] IO.popen

Il est également possible d'utiliser la commande IO.popen :

commande = IO.popen("uname -a") => #<IO:0x53963888>

On peut alors récupérer la sortie de la commande avec :

sortie = commande.readlines   #  => "Linux machine 2.6.22.5 #2 SMP Fri Aug 25 14:31:07 CEST 2006 i686 unknown\n"

Il est bien sûr possible de concaténer en une seule ligne avec :

sortie = IO.popen("uname -a").readlines   #  => "Linux machine 2.6.22.5 #2 SMP Fri Aug 25 14:31:07 CEST 2006 i686 unknown\n"

Et de concaténer la commande et les arguments :

arg = "-a"
sortie = IO.popen(["uname",arg].join(" ")).readlines     => "Linux machine 2.6.22.5 #2 SMP Fri Aug 25 14:31:07 CEST 2006 i686 unknown\n"

Mais cela devient plus compliqué qu'avec les simples quotes inversées.

[modifier] Commande system

On peut également utiliser la commande system. Elle retourne le code retour de la commande et affiche la sortie de la commande sur la sortie standard.

a = system("uname -a")   #   => true

et à l'écran :

Linux machine 2.6.22.5 #2 SMP Fri Aug 25 14:31:07 CEST 2006 i686 unknown

[modifier] Commande exec

La commande exec, exécute la commande puis quitte définitivement ruby, donc pas de retour

exec("uname -a")

Quitte le programme en affichant la sortie standard :

Linux machine 2.6.22.5 #2 SMP Fri Aug 25 14:31:07 CEST 2006 i686 unknown
bash $