Blog tech

Précédence des opérateurs en ruby et contrôle de flux

Rédigé par Martin Catty | 3 novembre 2015

Qu’est ce que la précédence ?

La précédence désigne la priorité et la force des différents opérateurs.

En mathématique les différents opérateurs ont des priorités différentes, par exemple la multiplication prend le pas sur l’addition.

De fait en ruby, comme dans bien d’autres langages, cette expression retourne le résultat attendu :

2 + 3 * 4
=> 14

Pour reprendre la main sur le flux des opérateurs on peut bien sûr utiliser des parenthèses :

(2 + 3) * 4
=> 20

Quels sont les opérateurs ?

Les opérateurs sont les suivants, de la précédence la plus élevée à la plus faible :

[] et []= Accès à un élément du tableau et set d'une valeur
** Exponentiel
! ~ + - Inversion, complément (remplace les bits 0 par 1 et vice-versa), plus et moins (-2 ou +3)
* / % Multiplication, division et modulo
+ - Addition et soustraction
<< >> Décallage de bit vers la droite ou gauche. Retire des bits à droite, ajoute des 0 à gauche. Ex: (9 >> 1).to_s(2)
& AND, opérateur bit à bit, 1 si 1 des deux côtés, 0 sinon. (9 & 14).to_s(2) donne en binaire 1001 & 1110 = 1000 (8)
^ | ^ est l'opérateur bit à bit XOR (ou exclusif). 1 si uniquement un des deux bits est à 1. | est l'opérateur bit à bit OR. 1 si 1 d'un côté au moins.
<= < > >= Opérateurs de comparaison
<=> == === != =~ !~ Comparaison d'égalité ou de matching
&& Et logique. Vrai si les des deux éléments sont évalués à vrai. `true && false` renvoie false.
|| Ou logique. Vrai si l'un des deux éléments est évalué à vrai. `true || false` renvoie true.
.. ... Range incluant et excluant la borne haute : Ex `(1..3).to_a == [1, 2, 3]`. `(1...3).to_a == [1, 2]`
? : Ternaire. Ex: `i % 2 == 0 ? "even" : "odd"`
= += -= &&= etc… Affectations
defined? Vérifie la définition ou non d'une variable
not Négation logique
or and Composition logique
if unless while until Conditions et boucles
begin end Blocs

Contrôle de flux versus opérateurs logiques

La précédence des opérateurs peut avoir des conséquences importantes pour des opérateurs qui semblent avoir le même usage.

C’est le cas de && et and tout comme || et or. Prenons l’exemple suivant :

a = []
b = 1
c = a.empty? and b.is_a?(String)

Le dernier retour sera ici false et c sera true ce qui peut paraître suprenant. On le comprend mieux en prennant en compte la précédence. L’interpréteur ruby va «lire» l’expression de la sorte :

a = []
b = 1
(c = a.empty?) and b.is_a?(String)

En effet l’affectation a une précédence plus forte que le and comme on l’a vu dans le tableau ci dessus.

Avec le && logique le résultat est tout autre et plus proche de ce que l’on attend naturellement :

a = []
b = 1
c = a.empty? && b.is_a?(String)

Le retour est false et c également car l’expression se décompose de la sorte :

a = []
b = 1
c = (a.empty? && b.is_a?(String))

Il en est de même pour les opérateurs or et ||. Il faut en fait voir les opérateurs or et and comme des opérateurs de gestion de flux.

Leur usage est donc plus proche de celui de conditions telles que if, else ou unless que d’un opérateur logique.

Vous avez sans doute déjà vu en Perl ou en PHP des instructions telles que :

chdir '/foo/bar' or die "Can't cd to bar"

Le fonctionnement est le même en ruby et nous permet de gérer le flux de la sorte :

File.exists?('/tmp/bar') or File.new('/tmp/bar', 'w') {}

ce qui est comparable à :

File.new('/tmp/bar', 'w') {} unless File.exists?('/tmp/bar')

La connaissance des opérateurs et de leur précédence est un aspect important du langage. N’hésitez pas à partager vos retours d’expériences et pièges dans lesquels vous avez pu tomber.

L’équipe Synbioz.

Libres d’être ensemble.