Les méthodes

Nous allons maintenant aborder une notion importante : celle de méthode. Les méthodes nous permettent de factoriser notre code, c’est-à-dire d’écrire un bloc de code une seule fois pour l’utiliser à plusieurs endroits facilement. Elles permettent donc d’écrire du code plus rapidement et plus facilement.

Principe et schéma d’une méthode

Qu’est-ce qu’une méthode ?

Une méthode est un ensemble d’instructions permettant de réaliser une certaine tâche (additionner deux nombres, par exemple). Nous pouvons créer des méthodes pour faire tout et n’importe quoi. Si nous voulons faire une méthode repeatMessage qui affiche un message 10 fois, nous pouvons. Les méthodes puts et print affichent des messages, par exemple.

Lorsque nous utilisons une méthode dans un programme, on dit que l’on appelle cette méthode. Tout comme nous pouvons utiliser print autant de fois que nous le voulons, nous pouvons appeler n’importe quelle méthode autant de fois que nous le voulons dans notre programme.

Les méthodes permettent de n’écrire le code qu’une seule fois. Imaginons que nous ayons un code de 20 lignes qui fasse une action, et que nous fassions cette même action à trois endroits différents de notre programme. Plutôt que de recopier le code à chaque fois, nous allons faire une méthode qui effectue cette action et l’appeler à chaque fois. Ainsi, plutôt que d’écrire 60 lignes, nous allons écrire 23 lignes, 20 lignes pour la méthode, et une ligne par appel à la méthode (en fait ça fera 25 lignes).

On peut donc voir les méthodes comme des sous-programmes. Elles permettent de rendre le programme plus modulable.

Déclarer une méthode

Voyons maintenant comment déclarer une méthode en Ruby. Voici le schéma d’une méthode.

# Définition de la méthode add.
def add
  # Instructions.
end

La définition d’une méthode débute par le mot-clé def et se termine par end. Ici, nous avons déclaré la méthode add. Une fois qu’une méthode est déclarée, nous pouvons l’appeler dans notre programme comme n’importe quelle autre méthode. Par exemple, on pourrait appeler la méthode add de cette manière.

print 'J’appelle la méthode add : '
add
print 'C’est fait.'

Bien sûr, si une méthode n’est pas définie, nous ne pouvons pas l’appeler et nous obtiendrons une erreur. Ainsi, ce code ne fonctionnera pas.

a = 4
b = 5
hello

Le code qui va suivre est par contre parfaitement valable. La méthode hello, même si elle ne fait rien du tout, y est définie.

def hello
  # Instructions.
end

a = 4
b = 5
hello

Une méthode doit être définie avant son utilisation.

Ainsi, ce code ne fonctionne pas.

a = 4
b = 5
hello

def hello
  # Instructions.
end

On pourrait pourtant penser que ce code est le même que le précédent. Cependant, quand l’interpréteur arrive à hello, il ne connaît pas encore hello, et donc on obtient une erreur.

En Ruby, il est conseillé d’écrire les noms des méthodes en convention « snake_case » (comme les noms de variable).

Écrire des méthodes

Nous allons maintenant écrire nos premières vraies méthodes. Certaines méthodes retournent une valeur particulière (la méthode gets, par exemple, retourne la chaîne de caractères entrée par l’utilisateur). On peut donc demander à nos méthodes de retourner une valeur particulière, mais ce n’est pas obligatoire. Celles qui n’en renvoient pas renvoient nil. Ce sont ces méthodes que nous allons voir, pour commencer.

Procédure

Nous avons dit qu’une méthode qui ne retourne pas de valeur particulière retourne nil. On peut alors voir nil comme une valeur par défaut qui signifie qu’aucune valeur réelle n’est renvoyée (il faut comprendre que nil est renvoyée si le concepteur de la méthode n’a spécifié aucune valeur de retour). La méthode print, par exemple, renvoie nil. Pour voir cela, retournons sur IRB et tapons print.

> print
=> nil

Une méthode qui retourne nil s’appelle une procédure. Prenons l’exemple d’une méthode qui affiche « Hello World! ».

# Définition de la méthode bonjour.
def hello
  print 'Hello World!'
end

hello # On appelle la méthode. Elle affiche « Hello World! ».

Cette procédure ne fait qu’un bête affichage. Nous pouvons bien sûr changer cet affichage par ce que nous voulons et mettre autant d’instructions que nous le voulons dans notre méthode.

Les procédures nous permettent d’éviter de devoir réécrire plusieurs fois des instructions identiques que nous utilisons souvent : nous écrivons une fois la procédure et nous l’appelons autant de fois que nous le voulons.

Valeur de retour

Les procédures sont très utiles, mais une méthode qui renvoie quelque chose, c’est tout aussi bien. Pour étudier ce type de méthodes, nous allons reprendre l’exemple du début de ce chapitre : nous allons écrire une méthode qui permet d’additionner deux nombres (nous l’appellerons add). Cette méthode devra retourner le résultat de l’addition. Voici comment cela se fait.

# Définition de la méthode add.
def add
  x = 9
  y = 5
  return x + y # Retour.
end

print add # Nous devons utiliser print pour afficher la valeur de retour.

Nous avons vu comment nous retournons la valeur : avec le mot-clé return. return x + y permet donc de retourner la valeur de x + y. Dans cet exemple, add retourne x + y, soit 9 + 5, et donc 14. print add signifie afficher la valeur retournée par add, c’est-à-dire afficher x + y et donc, ici, 14.

Le return n’est pas obligatoire. Nous aurions parfaitement pu écrire ceci.

def add  
  x = 9
  y = 5
  x + y # Retour.
end

print add

Le mot-clé return met fin à la méthode. Les instructions après le return ne seront pas exécutées.

Ainsi, dans ce code, le second print ne sera pas exécuté.

def hello
  print 'Bonjour.'
  return 0
  print 'Merci.'
end

En fait, on peut utiliser return sans aucune valeur. Dans ce cas, aucune valeur ne sera retournée, le return servira juste à mettre fin à la méthode. On peut donc écrire ceci.

def hello
  print 'Bonjour.'
  return
  print 'Merci.'
end
Redéfinition de méthode

Quand on définit des méthodes, il faut faire attention. Si on définit plusieurs fois la même méthode, la version qui sera retenue quand on appellera la méthode sera la dernière que l’on aura définie. Ainsi, dans le code suivant, la méthode hello fera deux choses différentes.

def hello
  puts 'Bonjour.'
  return 5
end

puts hello

def hello
  print 'Bonjour, utilisateur.'
  return 10   
end

print hello

Le premier appel à hello affiche « Bonjour. » et retourne 5. Le second appel à hello affiche « Bonjour, utilisateur. » et retourne 10. Finalement, ce code affiche ceci.

Bonjour.
5
Bonjour, utilisateur.10

Il faut donc faire attention à comment on définit nos méthodes. En fait, il vaut mieux ne jamais redéfinir une méthode et juste faire attention aux noms qu’on leur donne.

Les paramètres

Tout à l’heure, nous avons affiché « Bonjour » suivi de rien ou de « utilisateur » en redéfinissant la méthode. Nous allons maintenant voir la façon correcte de faire ceci grâce aux arguments des méthodes.

Les arguments sont des paramètres qui peuvent influer sur l’exécution d’une méthode. Les méthodes print et puts, par exemple, prennent en argument des données à afficher. En fonction des données passées, ce n’est pas la même chose qui est affichée.

Méthodes à un argument

Commençons par voir comment passer un argument à une méthode. Nous devons juste ajouter le nom que nous voulons donner à notre paramètre après le nom de la méthode. Nous pouvons le mettre entre parenthèses. Ce n’est pas obligatoire, mais c’est conseillé et nous le ferons (en fait, nous pouvons aussi utiliser les parenthèses pour les procédures et écrire def f(), mais c’est déconseillé). Voici le squelette d’une méthode avec argument.

def method_name(arg_name)

end  

Nous pouvons alors utiliser l’argument dans notre méthode comme n’importe quelle autre variable. Ainsi, le bon code pour la méthode hello serait celui-ci.

def hello(name)
  puts "Bonjour #{name}."
end

hello('utilisateur')
hello('')

On obtient alors ceci.

Bonjour utilisateur.
Bonjour .

Notons que l’on appelle alors la méthode en écrivant l’argument à côté entre parenthèses. Là encore, les parenthèses ne sont pas obligatoires, mais elles sont conseillées lorsque nous utilisons des méthodes que nous avons écrites. Elles sont déconseillées dans de rares cas (par exemple avec les méthodes comme print et puts) Bien sûr, si nous n’appelons pas la méthode en lui passant le bon nombre d’arguments, nous aurons une erreur.

Les arguments ne peuvent pas être utilisés en dehors de la méthode. Ils sont définis uniquement dans la méthode.

Ainsi, ce code provoquera une erreur.

def hello(name)
  puts "Bonjour #{name}."
end

hello('utilisateur')
hello('')

print name
Méthodes à plusieurs arguments

Les méthodes à plusieurs arguments ne sont pas plus compliquées à utiliser. La seule différence avec les méthodes à un argument est que nous devons utiliser une virgule pour séparer les arguments, autant dans la définition de la méthode que dans son appel. Les règles à suivre restent les mêmes. Le squelette de la méthode devient alors celui-ci.

def method_name(arg1_name, arg2_name, arg3_name, argn_name)

end  

Ce n’est pas vraiment plus compliqué que les méthodes à un argument.

Reprenons l’exemple de la méthode add. Les arguments permettent de définir la valeur de x et de y lors de l’appel de la méthode. Comme ceci.

def add(x, y)
  return x + y
end

print add(4, 5) # Nous affichons la valeur retournée par add(4, 5).

Là encore, le nombre d’arguments lors de l’appel doit être le même que lors de la définition de la méthode. Dans notre cas, il n’y en que deux : x et y.

Valeurs par défaut

Nous avons dit qu’il fallait passer le bon nombre d’arguments à la méthode lors de son appel. Néanmoins, ce serait bien de pouvoir se passer de certains arguments lors de certains appels, non ? Par exemple, pour notre méthode hello, de ne pas avoir à passer une chaîne vide en paramètre lorsque nous ne voulons pas afficher de nom.

Nous sommes fiers d’annoncer que nous pouvons faire tout cela en donnant à notre argument une valeur par défaut lorsque nous définissons notre méthode. En faisant cela, lorsque nous appellerons la méthode sans lui donner cet argument, sa valeur sera la valeur par défaut.

Pour définir une méthode avec une valeur par défaut, il faut écrire l’argument dans la définition de la méthode en lui donnant déjà une valeur. Notre méthode héllo s’écrit alors ainsi.

def hello(name = '')
  puts "Bonjour #{name}."
end

hello('utilisateur')
hello 

Nous obtenons bien le résultat attendu.

Nous pouvons faire des méthodes avec plusieurs arguments facultatifs. Cependant, ces derniers doivent être les derniers paramètres de notre méthode.

Quand on y réfléchit, c’est logique. Si on crée une méthode comme ceci (def f(arg1, arg2 = 10, arg3), comment savoir à quoi correspond 12 dans cet appel : f(3, 12). Ici, on devine qu’il correspond au troisième argument, mais dans d’autres cas, nous voyons bien que c’est impossible.

Notons tout de même que nous pouvons choisir l’ordre dans lequel nous transmettons nos paramètres à une méthode. Il faut dans ce cas préciser le nom de l’argument.

Regardons ce code, par exemple.

def hello(last_name, first_name)
   puts "Bonjour #{last_name} #{first_name}."
end

hello('moi', 'utilisateur')
hello(first_name = 'utilisateur', last_name = 'moi')

L’argument first_name vaut "utilisateur" dans les deux cas et l’argument last_name vaut "moi" dans les deux cas. Les deux appels à la méthode donnent donc le même résultat.

Exercices

Exercice 1

Voici le premier exercice : faire une calculatrice ! Nous devons gérer les quatre opérations usuelles : l’addition, la soustraction, la multiplication et la division. L’utilisateur devra choisir une opération, puis saisir deux nombres. Nous afficherons le résultat de l’opération. Voici un exemple de ce que doit afficher notre programme.

Choisissez une opération.

1. Addition.
2. Soustraction.
3. Multiplication.
4. Division.

Votre choix : 3

Vous avez choisi la Multiplication.

Entrez le premier nombre : 4
Entrez le second nombre : 23

4.0 * 23.0 = 92

Correction.

def menu
  puts 'Choisissez une opération.'
  tab = %w[Addition Soustraction Multiplication Division]
  tab.each_with_index { |op, idx| puts "#{idx + 1}. #{op}." }
  choice = 0
  while choice < 1 || choice > 4
    print "\nVotre choix : "
    choice = gets.chomp.to_i
  end
  puts "\nVous avez choisi de faire une #{tab[choix - 1]}."
  return choice
end

choice = menu
print "\nEntrez le premier nombre : "
number1 = gets.chomp.to_f
print 'Entrez le second nombre : '
number2 = gets.chomp.to_f
puts
if choix == 1
  print "#{number1} + #{number2} = #{number1 + number2}"
elsif choix == 2
  print "#{number1} - #{number2} = #{number1 - number2}"
elsif choix == 3
  print "#{number1} * #{number2} = #{number1 * number2}"
elsif number2 != 0
  print "#{number1} / #{number2} = #{number1 / number2}"
else
  print 'Diviser par 0, c’est mal.'
end
Exercice 2

Exercice un peu plus difficile cette fois. Notre programme doit demander deux nombres à l’utilisateur et afficher le PGCD de ces deux nombres. Nous pouvons aller faire un tour sur la page Wikipédia du PGCD pour nous rafraîchir la mémoire.

Correction.

def pgcd(a, b)
  if a > b
    tmp = a
    a = b
    b = tmp
  end
  if a == 0
    return b
  end
  while b % a != 0
    tmp = b % a
    b = a
    a = tmp
  end
  return a
end

On peut aussi faire cette fonction de manière récursive en utilisant le fait que le PGCD de deux entiers est le PGCD du plus petit entier et du reste de la division euclidienne de ces deux entiers, les cas terminaux étant celui où l’un des deux nombres est nul (auquel cas le PGCD est l’autre nombre) et celui où lorsque le reste est nul (le PGCD est alors le plus petit des nombres).

def pgcd(a, b)
  return b if a == 0
  return a if b == 0
  r = a % b
  r == 0 ? b : pgcd(b, r)
end

En fait, la méthode gcd qui s’applique à un entier nous permet déjà d’avoir le PGCD de deux nombres (et oui, la boîte à outils de Ruby est très complète). Ainsi, 36.gcd(30) renvoie 6.

Exercice 3

Nous allons finir par faire un convertisseur. Nous devons demander à l’utilisateur de choisir une conversion et afficher le résultat de cette conversion. Voici les conversions que la première version de notre programme doit gérer (dans les deux sens bien sûr) :

  • conversion de degrés en radians (1 radian = 180/π degrés) ;
  • conversion de km/h en m/s (1 m/s = 3.6 km/h).

Ces deux conversions sont très utiles en physique. Nous devons également réaliser un menu dans lequel nous proposerons à l’utilisateur de réaliser une des conversions ou de quitter le programme.

Correction.

Le but des méthodes est d’avoir un code bien découpé. Utilisons-les donc pour avoir un beau programme (en plus, c’est un peu le but, vu le nom du chapitre).

def menu 
  puts '1. Convertir des radians en degrés.'
  puts '2. Convertir des degrés en radians.'
  puts '3. Convertir des kilomètres par heure en mètres par seconde.'
  puts '4. Convertir des mètres par seconde en kilomètres par heure.'
  puts '5. Quitter.'
  choice = 0
  while choice < 1 or choice > 5
    print "\nVotre choix : "
    choice = gets.chomp.to_i
  end
  return choice
end

PI = 3.14159265359
DEG_TO_RAD = 180 / PI
MS_TO_KMH = 3.6
choice = menu
if choice != 5
  print "\nEntrez le number : "
  number = gets.chomp.to_f
  if choice == 1
    print number * DEG_VERS_RAD
  elsif choice == 2
    print number / DEG_VERS_RAD
  elsif choice == 3
    print number / MS_VERS_KMH
  else
    print number * MS_VERS_KMH
  end
end

Maintenant que nous avons réalisé ce programme, nous pouvons le compléter en rajoutant quelques autres conversions :

  • conversion de pieds en mètres (1 pied = 0.3048 mètres) ;
  • conversion de grammes en livres (1 gramme = 0.002205 livres) ;
  • conversion de degrés Fahrenheit en degrés Celsius (température en degrés Fahrenheit = 32 + 1.8 × température en degrés Celsius) ;
  • conversion de diverses devises (euro, dollar, livre, etc.).

Nous pouvons même (et c’est un bon entraînement) faire un programme de conversion d’un nombre vers une autre base. Gérer le binaire, l’octal, le décimal et l’hexadécimal est suffisant pour commencer, mais notre programme devrait à terme être capable de convertir un nombre de n’importe quelle base en n’importe quelle autre base.

En cas de pépin, n’oublions pas que le forum est là pour nous aider.


C’est la fin de ce chapitre. Nous savons maintenant suffisamment de choses pour bien faire nos méthodes.

Nous pouvons reprendre l’exercice de compression du chapitre précédent et l’améliorer en utilisant les méthodes. Nous pouvons écrire une méthode qui compresse et une autre qui décompresse… Nous pouvons même rendre notre calculatrice plus performante. Dans tous les cas, il est nécessaire de pratiquer un peu avant de passer à la suite. Les méthodes sont un élément très important qu’il faut maîtriser.

  • Les méthodes permettent d’éviter de réécrire le même code plusieurs fois.
  • Les méthodes peuvent retourner des valeurs, une méthode retournant nil étant une procédure.
  • Les méthodes peuvent prendre des arguments, dont certains peuvent être facultatifs.