Comme tous les langages de programmation, Ruby utilisent des variables booléennes pour évaluer la véracité des expressions, notamment dans les instructions conditionnelles comme if, while...
Ruby a toutefois sa propre logique qu'il faut connaitre pour en tirer parti et pour éviter les erreurs, il a aussi son propre dialecte qui simplifie les écritures.

1. Classes et valeurs  booléennes

Ruby ne comporte pas une classe pour les valeurs booléennes, mais trois : TrueClass, FalseClass et NilClass. Chacune comprend une et une seule valeur prédéfinie, respectivement: true, false et nil.

1.1. Valeur nil

Sémantiquement, la valeur nil signifie que l'élément associé n'est pas définie.

Il est possible de transformer la valeur nil en l'élément neutre des ensembles de valeurs des classes usuelles :

  • nil.to_a → [] tableau vide
  • nil.to_i → 0 nombre entier nul
  • nil.to_f → 0.0 nombre flottant nul
  • nil.to_s → "" chaine vide

Tout objet possède la méthode nil? qui permet de tester si un élément a une valeur définie ou non, c'est-à-dire est égale à nil ou à tout autre chose.

Rails définit aussi sur les objets des méthodes qui analysent le contenu :

  • blank? qui est vraie si l'élément est blanc ou vide, donc de valeur égale à nil, une chaine vide ou blanche, un tableau ou un hash vide ;
  • present? qui est la fonction opposée, donc fausse dans les mêmes cas.

NB : Même pour prendre la valeur nil, une variable doit être définie. On peut tester cette situation avec la fonction defined? décrite au chapitre suivant.

1.2. Valeur booléenne d'une variable

Les valeurs false et nil sont considérées comme fausses.
Toutes les autres valeurs, y compris 0 (zéro), "" (chaine vide), [] (tableau vide, etc., sont considérées comme vraies.

2. Expressions booléennes

Ruby disposent des trois fonctions booléennes de base - et, ou et non -, chacune en plusieurs versions, subtilement différentes :

  • fonction et → && and &
  • fonction ou → || or &
  • fonction ou exclusif → ^
  • fonction non → ! not

2.1. Différences entre les opérateurs booléens

Les différences entre les différentes formes sont les suivantes :

  1. &&, and, ||, or, !, not peuvent avoir les membres de l'expression de tous types ;
    &, |, ^ doivent avoir les deux membres de l'expression de l'une de trois classes booléennes énoncées au chapitre 1 pour être interprétés comme des opérateurs booléens. Ces trois opérateurs peuvent en effet avoir des comportements spécifiques pour certaines classes (ex. : intersection et union de tableaux, opérateurs bit à bit pour les nombres entiers).

  2. &&, and, ||, or évaluent le premier membre de l'expression et uniquement si nécessaire le second ;
    & et | évaluent toujours les deux membres de l'expression.
    La différence tient aux effets de bord potentiel si le deuxième membre contient une affectation ou l'appel d'une fonction qui modifie une variable.

  3. && et || renvoient le dernier membre utile et évalué d'une expression, pas sa valeur booléenne. Ex. : 'abc' || nil → 'abc' ;
    &, |, ! et not renvoient toujours une valeur booléenne (true ou false). Ex. : 'abc' | nil → true.

  4. La précédence de tous ces opérateurs, c'est-à-dire l'ordre dans lequel ils sont évalués dans une expression complexe, sont différentes (voir le tableau des précédences) ;

  5. !, &, | sont des méthodes qui peuvent être redéfinies pour chaque classe (ou pour toutes si elles sont définies sur la classe mère Object) ;
    tous les autres opérateurs ne peuvent pas être redéfinis.

Conseil : Sauf besoins particuliers et à vos risques et périls, limitez-vous aux trois opérateurs &&, || et !, et placez des parenthèses dans les expressions complexes !

2.2. Operateur defined?

On peut classer l'opérateur defined? parmi les opérateurs booléens, car il est parfois bien utile dans les expressions conditionnelles pour éviter les erreurs impromptues, notamment pour vérifier l'existence de variables de classes ou de paramètres de partiels.

defined?(x) vaut nil si x n'est pas défini. S'il l'est, alors defined?(x) vaut une chaine de caractères relative à la nature de x.
Dans une expression booléenne, defined?(x) peut donc être considérée comme vraie si x est défini, fausse sinon.

NB : Comme en Ruby "tout est objet", defined? peut être utilisé pour vérifier l'existance d'une méthode, d'une classe ou d'un module.

2.3. Méthode nil?

La classe Object contient la méthode nil? qui vérifie si un objet prend la valeur nil. Elle rend la valeur true dans ce cas, false sinon.

Quand on est certain du type de la valeur potentielle d'une variable et qu'elle ne peut prendre la valeur false, on peut utiliser cette variable dans une expression conditionnelle comme négation de nil?.
Exemple : if x est dans ce cas similaire à unless x.nil? (lui même équivalent à if !x.nil? - voir le chapitre 5.).
Le risque est une potentielle valeur false. On évitera donc cette forme, ntamment pour tester des paramètres d'appel.

3. Affectations

Les auto-affectations ||= et &&= tirent parti des propiétés 1, 2 et 3 du chapitre 2.1 pour la forme dialectique suivante :

param ||= default permet d'assigner à la variable param la valeur de default si param n'est pas vrai, c'est-à-dire si elle n'est pas définie et si elle ne prend pas l'une des valeurs nil ou false. À noter qu'après cette instruction, param sera toujours définie, éventuellement à nil ou false suivant la valeur de default.

param &&= nouveau est beaucoup moins fréquent. Cette forme permet de remplacer la valeur de param par celle de nouveau uniquement si param est défini par autre chose que nil ou false. Là aussi, après cette instruction param sera toujours définie et contiendra nil si l'affectation a échoué.

Ces formes peuvent être étendues. Par exemple :

param ||= default1 || default2 : si param n'est pas définie ou vaut nil ou false, lui affecter la valeur de default1 ; si celle-ci vaut nil ou false, lui affecter alors la valeur de default2.

On peut aussi trouver dans une boucle l'expression :

(params ||= []) << param : si params n'est pas définie, l'initialiser avec un tableau vide, puis dans tous les cas y ajouter la valeur de param. L'initialisation du tableau aurait dû être réalisée en dehors de la boucle ou par une instruction conditionnelle complexe.

4. Opérateurs de comparaison

Ruby connait dix opérateurs de comparaison : ==   ===   <=>   <   <=   >   >=   =~   !=   !~ 

==   <   <=   >   >= sont les opérateurs de comparaison habituels.
Tous retournent toujours l'une des deux valeurs booléennes true ou false.

=== est utilisé dans l'instruction case target when à la place de ==. Il s'agit la plupart du temps de la même méthode.
Il retourne toujours l'une des deux valeurs booléennes true ou false.

<=> est utilisé notamment pour les tris des objets d'une classe.
Il retourne en général -1, 0 ou 1 ; ce n'est donc pas un opérateur booléen, car ces trois valeurs sont associées à la valeur true.

=~ teste une chaine à une expression régulière.
Il retourne nil si la chaine ne respecte pas l'expression régulière, sinon l'indice du premier caractère où celle-ci est vérifiée. Ces valeurs sont transposées respectivement dans les valeurs false et true.

!=   !~ sont les négations de == et de =~ .
Contrairement aux autres opérateurs, ce ne sont pas des méthodes ; ils ne peuvent donc pas être redéfinis.
a != b est équivalent à !(a == b), et a !~ b est équivlent à !(a =~ b).
Les deux opérateurs retournent toujours l'une des deux valeurs booléennes true ou false.

5. Expressions conditionnelles

Les expressions booléennes sont le plus souvent utilisées dans les expressions conditionnelles.

En Ruby, toutes les expressions renvoient une valeur. Elles peuvent donc intervenir dans des expressions plus complexes, comme des affectations.
Exemple : html = '<h1>' + (if page == 1 then 'Première' else 'Autre' end) + ' page</h1>'

5.1. Expressions if et unless

Syntaxe de l'expression if :

if condition [ then ]
   body
[ elsif condition [ then ]
   body , ... ]
[ else
   body ]
end

Syntaxe de l'expression unless :

unless condition [ then ]
   body
[ else
   body ]
end

unless est l'opposé de if : unless condition est équivalent à if !condition.

Le mot-clé then n'est obligatoire que si la clause suivant est sur la même ligne.

Modificateurs if et else :

if et unless peuvent être aussi utiliser comme des modificateurs sous l'une des deux formes, très fréquentes quand il n'y a pas de partie alternative elsif ou else :

instruction if condition
instruction unless condition

L'instruction n'est évaluée que si la condition est respectivement vraie ou fausse.

NB : Si l'instruction modifiée est une affectation, la ou les variables à gauche du signe égal (=) seront définies et initialisées à la valeur nil si elle n'étaient pas définies avant. Ce comportement est général et s'applique aussi en particulier aux affectations internes des expressions conditionnelles.

Une expression conditionnelle retournera nil si la condition n'est pas remplie et si elle ne comporte pas de clause else.

5.2. Opérateur ternaire ? :

Syntaxe :    condition ? expr1 : expr2

Il est équivalent à   if condition then expr1 else expr2 end

Rien n'empêche qu'expr1 ou expr2 soient des expressions complexes commes des affectations, voire qu'elles comprennent d'autres opérateurs ternaires.

5.3. Expressions case

Il existent deux formes d'expressions case qui chacune ont leur équivalent en expression if.

La première forme enchaine plusieurs conditions :

case
when condition [, condition ]... [ then ]
   body
when condition [, condition ]... [ then ]
   body
...
[ else
   body ]
end

La seconde forme teste une expression cible à plusieurs valeurs :

case target
when comparison [, comparison ]... [ then ]
   body
when comparison [, comparison ]... [ then ]
   body
...
[ else
   body ]
end

Chaque test exécute une expression comparison === target.

Une expression testée peut être un tableau précédé d'une étoile (*) : chaque élément du tableau sera alors testé tour à tour.

Comme pour if et unless, une expression case retournera nil si la condition n'est pas remplie et si elle ne comporte pas de clause else.

6. Boucles

Les expressions booléennes servent à terminer les boucles.

En pratique, les boucles while, until et for sont assez peu utilisées, car elles sont le plus souvent remplacées par un itérateur appliqué sur un objet énumérable (un itérateur est une méthode de l'objet dont le nom commence souvent par each).
Il y a une différence notable entre ces deux types de boucles : les variables locales définies dans le corps de la boucle le restent en dehors, ce qui n'est pas le cas pour celles définies dans le bloc d'un each (ou d'un loop).

6.1. Boucles while et until

Syntaxe d'une boucle while :

while condition [ do ]
   body
end

Syntaxe d'une boucle until :

until condition [ do ]
   body
end

Modificateurs while et until :

while et until peuvent être utilisés comme modificateurs avec l'une des deux syntaxes suivantes :

expression while condition
expression until condition

6.2. Boucle for

for name [, name ]... in expression [ do ]
  body
end

Cette instruction est peu utilisée, car on lui préfère la version plus objet, presqu'équivalente :

expression.each do | name [, name ]... |
   body
end

6.3. Boucle loop

loop do
   body
end

Pour se terminer, une boucle loop doit contenir une instruction break.

loop n'est pas une instruction du langage, mais un itérateur du module Kernel. Elle est donc suivie d'un vrai bloc do end dont les variables locales sont internes.

6.4. break, redo, next et retry

break, redo, next et retry modifient le cours de l'exécution d'une boucle ou d'un itérateur.

  • break sort immédiatement de la boucle ;
  • redo recommence en début de boucle sans exécuter la condition de la boucle ;
  • retry recommence en début de boucle en exécutant la condition ;
  • next saute en fin de boucle, ce qui conduit à exécuter la condition.

break peut être suivi d'un ou plusieurs arguments qui sont retournés comme valeur de retour, sous forme d'un tableau s'il y en a plusieurs.

version 0.8.3-0226-120923

↑ Haut