Programmation Ruby/Contrôle

Méthodes logiques

modifier

Une expression booléenne est toute expression qui renvoie après évaluation vrai (true) ou faux (false). Une valeur booléenne peut être de type booléen (true ou false)

Attention:

  • 0 est true => Objet Fixnum de valeur 0
  • "" est true => Objet String de valeur ""
  • 'False' est true => Objet String de valeur 'False'

Il s'agit ici d'une autre des caractéristiques des langages 'tout objet' ou même les nombres sont des objets. Tout objet est true.


Une autre caractéristique de Ruby et de tous les langages "tout objet", est que les "opérateurs", décrit ci-dessous, comme on les appelle dans les langages orientés objet ou procéduraux, sont en réalités des méthodes d'instance. En règles générale, le développeur doit implémenter lui même ses méthodes. Néanmoins certaines d'entre, dont les equivalents d'"opérateurs", sont pré-définies dans la classe Object, mère de toutes les autres classes, ou encore dans un module. Nous reviendrons sur ces concepts dans le chapitre traitant de la programmation objet.

Les exemples, ici, utilisent des objets "standards" pour un souci de clarté, ils peuvent être remplacés par des expressions placées entre parenthèses. Ainsi :

42 == 42               => true

équivaut à

(40 + 2) == (21 * 2)   => true

defined?

modifier

defined? permet de vérifier l'existence d'une variable, l'opérateur renvoie nil si la variable n'existe pas, sinon renvoi une description :

defined? 42            => "expression"
defined? toto          => nil
defined? 42.times      => "method"
defined? $_            => "global-variable"
...

eql? teste si 2 variables ou valeurs sont égales et du même type, renvoi true si c'est le cas, sinon false.

42.eql?(42)            => true
42.eql?(18)            => false # Valeurs différentes
42.eql?(42.0)          => false # Types différents, en effet 42 est un entier, 42.0 un flottant

equal? renvoie true si les deux objets comparés sont en réalité un même objet (même id), sinon renvoie false.

a = "mon objet"
b = a

a.equal?(a)            => true
a.equal?(b)            => true
c = "mon objet"
a.equal?(c)            => false
42.equal?(42)          => true
"uv".equal?("uv")      => false

== permet de tester l'égalité entre les valeurs de deux éléments :

42 == 42               => true
42 == 23               => false
42 == "42"             => false

!= permet de tester l'inégalité entre les valeurs de deux éléments :

42 != 42               => false
42 != 23               => true
42 != "42"             => true

<=> sert à comparer deux variables, cette méthode n'est pas à proprement parler un opérateur booléen. En effet celle-ci renvoie -1, 0 ou 1 si, le premier élément est respectivement inférieur, égal ou supérieur au second, et renvoie nil si les types sont différents.

2 <=> 2                => 0
2 <=> 3                => -1
42 <=> 18              => 1
42 <=> "42"            => nil
42 <=> 42.0            => 0 # contrairement à eql?, c'est la valeur qui est comparée

Pour deux valeurs numériques cet opérateur retourne donc le signe de la différence entre celles-ci.

< et <=

modifier

< et <= correspondent respectivement à "inférieur" et "inférieur ou égal à" :

42 < 43                => true
42 < 42                => false
42 <= 42               => true
"abcdef" < "abzd"      => true       #la comparaison entre chaînes de caractères se fait lettre à lettre
"abzd"   < "abcdef"    => false 

> et >=

modifier

> et >= correspondent respectivement à "supérieur à" et "supérieur ou égal à" :

42 > 43                => false
42 > 42                => false
42 >= 42               => true
"abcdef" > "abzd"      => false       #la comparaison entre chaînes de caractères se fait lettre à lettre
"abzd"   > "abcdef"    => true

not et !

modifier

not et ! exprime la négation, l'opérateur "inverse" l'élément placé juste après, ainsi :

not true               => false
(!false)                 => true

De même, on peut exprimer la négation sur une expression si celle-ci se trouve entre parenthèses :

(!(2 < 10))              => false
not (2 < 10)             => false

and et &&

modifier

and et && correspondent au "et logique" :

true and false           => false
true and true            => true

Dans un souci d'optimisation, Ruby teste la première expression, et si celle-ci est fausse, il ne teste pas la seconde.

or et ||

modifier

or et || correspondent au "ou logique"

true or false            => true
true or true             => true
false or false           => false

Dans un souci d'optimisation, Ruby teste la première expression, et si celle-ci est vraie, il ne teste pas la seconde.

Les expressions conditionnelles

modifier

Une expression conditionnelle sert à réagir en fonction d'un élément donné.

Expression IF...THEN...ELSE

modifier

En algorithmique :


SI <EXPRESSION CONDITIONNELLE> ALORS 
  EXPRESSION1
[SINON
  EXPRESSION2
]
FIN SI

se traduit en Ruby par :


if <EXPRESSION CONDITIONNELLE> [then]
  EXPRESSION1
[else
  EXPRESSION2
]
end

La condition peut être une expression, à condition que celle-ci soit une expression booléenne (vrai ou faux), ou une valeur numérique (0 ayant valeur de faux).

donc :

if (1)

équivaut à

if (true)

qui équivaut à

if (2 + 2 == 4)

de même :

if (0)

équivaut à

if (false)

qui équivaut à

if (2 + 2 == 5)

Une expression conditionnelle peut être l'élément ouvrant d'un bloc, ou succéder à une expression si celle-ci tient sur une ligne, dans ce denier cas on ne peut utiliser else :

if true
  puts "true"
end                        => "true"

équivaut à

puts "true" if true        => "true"

Le pendant de if est unless (sauf si) et suit les mêmes règles, toutefois l'instruction n'est exécuté que si la condition est fausse :

puts "false" unless false   => "false"

À noter que pour faciliter la lecture, ruby permet de faire suivre l'expression testée du mot clef then :

if (true) then

Expression CASE...WHEN

modifier

SI <EXPRESSION CONDITIONNELLE>
  VAUT EXPRESSION 1 ALORS EXPRESSION RESULTANTE 1
  VAUT EXPRESSION 2 ALORS EXPRESSION RESULTANTE 2
  ...
  VAUT EXPRESSION N ALORS EXPRESSION RESULTANTE N
SINON EXPRESSION RESULTANTE FIN SI

ce qui se traduit en ruby par :

case <EXPRESSION CONDITIONNELLE>
  when EXPRESSION 1 then EXPRESSION RESULTANTE 1
[  when EXPRESSION 2 then EXPRESSION RESULTANTE 2
  ...
  when EXPRESSION N then EXPRESSION RESULTANTE N
else EXPRESSION RESULTANTE] end

case va comparer l'expression le suivant avec les expressions passées après les mots-clefs when. Si l'une d'elle correspond, case appelera l'expression suivant le then. Sinon il évaluera l'expression suivant le else :

chaine = "ça va ?"

case chaine
  when "bonjour" then "bonjour"
  when "ça va ?" then "oui merci"
  else "au revoir"
end                            => "oui merci"

À noter que l'expression suivant le case est facultative :

age = 40

case
  when ((age > 60) and (age < 100)) then "Vous êtes agé"
  when ((age <= 60) and (age > 15)) then "Vous êtes dans la fleur de l'age"
  when age <= 15 then "Vous êtes jeune"
  else "Mathusalem"
end                            => "Vous êtes dans la fleur de l'age"

Si age avait été supérieur à 100, c'est la condition else qui aurait été prise en compte.

À noter que then peut être remplacé par le caractère deux points ( : ), on peut également mettre la clause when sur deux lignes :

case 
  when ((age > 60) and (age < 100))
    "Vous êtes agé"
...
end

Les boucles

modifier

Les boucles permettent de parcourir une liste d'éléments, ou d'effectuer une action tant qu'une condition est respectée.

While/Until

modifier

TANT QUE <EXPRESSION CONDITIONNELLE> FAIRE
  EXPRESSION1
FIN TANT QUE

ce qui se traduit en ruby par

while <EXPRESSION CONDITIONNELLE>
  EXPRESSION1
end

Par exemple :

i = 0

while (i < 5) 
   puts i
   i = i + 1
end
                                            =>  0
                                                1
                                                2
                                                3
                                                4

Dans cet exemple l'expression conditionnelle est évaluée avant l'évaluation du corps de la boucle, donc si i avait été égal à 6, le contenu de la boucle n'aurait jamais été évalué. Néanmoins nous pouvons utiliser une autre construction commençant par un bloc et suivit de while, dans ce cas là le corps de la boucle est évalué au moins une fois :

i = 12

begin
  puts i
  i = i + 1
end while (i < 5)
                                           =>  12

La négation de while est until, que l'on pourrait traduire par "jusqu'à" :

i = 0

until (i > 5) 
   puts i
   i = i + 1
end
                                            =>  0
                                                1
                                                2
                                                3
                                                4
                                                5

loop ressemble à la structure while, mais ne prend pas d'expression conditionnelle. En fait loop correspond à

while(true)

Le seul moyen de quitter la boucle est d'utiliser l'instruction break :

i = 0

loop do
  puts i
  i = i + 1
  break if i >= 5
end
                                          => 0
                                             1
                                             2
                                             3
                                             4

La boucle for permet d'itérer à travers un ensemble :

for i in 5..8
   puts i
end
                                         =>  5
                                             6
                                             7
                                             8

Nous verrons les intervalles (de forme x..y) dans le chapitre suivant, pour l'heure il suffit de savoir qu'ils représentent tous les éléments compris entre x et y.

Ceci est une manière d'itérer, néanmoins nous verrons dans les chapitres suivant qu'en ruby il vaut mieux itérer en utilisant les méthodes adaptées. Ainsi le même exemple aurait pu s'écrire :

(5..8).each do |i|
  puts i
end

Pour les chaînes de caractères, le retour chariot servira de séparateur :

for ligne in "première\ndeuxième\ntroisième"
  puts ligne
end
                                         =>  première
                                             deuxième
                                             troisième

Itérateurs

modifier

un grand nombre d'objet en Ruby implémente des itérateurs, cad; des méthodes de parcours des éléments de l'objet lui-même.

La boucle each (chaque en anglais) parcours tous les éléments d'un tableau en assignant à une variable temporaire l'élément actuel.

Soit dans une classe la va

noms = ['toto', 'tata', 'titi']
noms.each do |nom|
  puts "Salut #{nom} !"
end
                                         => Salut toto !
                                         => Salut tata !
                                         => Salut titi !

each_byte et each_line

modifier

Les chaînes de caractères ont une méthode spécifique appelée each_byte (chaque caractère) qui parcours la chaîne caractère par caractère :

"abcdef".each_byte{ |caractere|  printf "%c\n", caractere }
                                              a
                                              b
                                              c
                                              d
                                              e
                                              f

Et each_line (chaque ligne) qui parcoure les lignes séparés par le retour chariot :

["première", "suite", "autre"].join("\n").each_line{|ligne| puts ligne}
                                         =>  première
                                             suite
                                             autre

Fonction typique à Ruby, la méthode d'itération des classes d'entiers nommée times (fois) et qui peut être utilisé avec les constantes numériques (ce langage étant pur objet) :

3.times{puts "texte"}
                                         =>   texte
                                              texte
                                              texte

ou bien :

3.times do
  puts "texte"
done
                                         =>   texte
                                              texte
                                              texte

Les mots clés de contrôle d'execution

modifier

Parmi les rares mots clés du langage, on trouve un certain nombre de controles d'execution

Nous avons déjà vu break qui permet d'interrompre l'exécution d'une boucle :

i = 0

while (true)
  break if i > 3
  puts i
  i = i+1
end
                                    => 0
                                       1
                                       2
                                       3

redo va réévaluer le corps de la boucle, mais sans retester la condition, et sans utiliser l'élément suivant (dans un itérateur)

for i in 1..4
  print "#{i} "
  if i==2
    i=0
    redo
  end
  puts i
end
                                    => 1 1
                                       2 0 0
                                       3 3
                                       4 4

La variable i étant définie localement (dans la boucle), cela ne change pas le déroulement par rapport à la liste globale, mais la variable est bien vue comme ayant une valeur différente de 2 la seconde fois et le redo est évité. Le puts suivant le redo dans le bloc de code de la boucle n'est pas exécuté lorsque celui est exécuté.

next va aller à la fin de la boucle, puis recommencer l'itération avec l'élément suivant dans le cas d'un itérateur :

i=0

loop do
  i += 1
  next if i < 3
  puts i
  break if i > 4
end
                                    =>  3
                                        4
                                        5

retry recommence l'itération à son début, dans son état premier :

for i in 1..5
  puts i
  retry if i == 2
 end
                               =>  1
                                     2
                                     1
                                     2
                                      ...

On entre ici dans une boucle infinie.


Remarques

modifier

Boucles implicites

modifier

Le parcours des éléments d'un tableau dans Ruby est implicite lorsque on l'utilise comme variable d'assignation, comme c'est le cas pour les chaînes de caractère dans la majorité des langages :

puts [ "élément 1", "élément 2", "élément 3" ]
                                         =>   élément 1
                                              élément 2
                                              élément 3

On peut de la même façon extraire une partie d'un tableau simplement sans avoir à créer de boucle qui parcoure tous les éléments.

Exemple tiré du site officiel de Ruby

villes  = %w[ Londres
             Oslo
             Paris
             Amsterdam
             Berlin ]
visitees = %w[Berlin Oslo]

puts "J'ai toujours besoin " +
    "de visiter les " +
    "villes suivantes :",
    villes - visitees
                                         =>   Londres
                                              Paris
                                              Amsterdam

Cet exemple sort les éléments du tableau qui sont dans les villes mais pas dans visitées