08. Écrire vos propres méthodes
Comme nous l'avons vu, les boucles et les itérateurs nous permettent de faire la même chose (exécuter le même code) encore et encore. Cependant, parfois nous voulons faire la même chose plusieurs fois, mais à partir de différents endroits du programme. Par exemple, supposons que nous écrivons un programme de questionnaire pour un étudiant en psychologie. Compte tenu des étudiants en psychologie que je connais et des questionnaires qu'ils m'ont fournis, cela ressemblerait à ceci :
puts 'Bonjour, et merci de prendre le temps de m\'aider'
puts 'avec cette expérience. Mon expérience concerne'
puts 'la façon dont les gens se sentent à propos de la nourriture mexicaine.'
puts 'Pensez simplement à la nourriture mexicaine et essayez de répondre'
puts 'à chaque question honnêtement, par "oui" ou par "non".'
puts 'Mon expérience n\'a rien à voir avec le fait de faire pipi au lit.'
puts
# Nous posons ces questions, mais nous ignorons leurs réponses.
bonneReponse = false
while (not bonneReponse)
puts 'Aimez-vous manger des tacos ?'
reponse = gets.chomp.downcase
if (reponse == 'oui' or reponse == 'non')
bonneReponse = true
else
puts 'S\'il vous plaît, répondez par "oui" ou "non".'
end
end
bonneReponse = false
while (not bonneReponse)
puts 'Aimez-vous manger des burritos ?'
reponse = gets.chomp.downcase
if (reponse == 'oui' or reponse == 'non')
bonneReponse = true
else
puts 'S\'il vous plaît, répondez par "oui" ou "non".'
end
end
# Nous prêtons attention à *cette* question, cependant.
bonneReponse = false
while (not bonneReponse)
puts 'Faites-vous pipi au lit ?'
reponse = gets.chomp.downcase
if (reponse == 'oui' or reponse == 'non')
bonneReponse = true
if reponse == 'oui'
pipiAuLit = true
else
pipiAuLit = false
end
else
puts 'S\'il vous plaît, répondez par "oui" ou "non".'
end
end
bonneReponse = false
while (not bonneReponse)
puts 'Aimez-vous manger des chimichangas ?'
reponse = gets.chomp.downcase
if (reponse == 'oui' or reponse == 'non')
bonneReponse = true
else
puts 'S\'il vous plaît, répondez par "oui" ou "non".'
end
end
puts 'Juste quelques questions de plus...'
bonneReponse = false
while (not bonneReponse)
puts 'Aimez-vous manger des sopapillas ?'
reponse = gets.chomp.downcase
if (reponse == 'oui' or reponse == 'non')
bonneReponse = true
else
puts 'S\'il vous plaît, répondez par "oui" ou "non".'
end
end
# Posez beaucoup d'autres questions sur la nourriture mexicaine.
puts
puts 'DÉBRIEFING :'
puts 'Merci d\'avoir pris le temps d\'aider avec'
puts 'cette expérience. En fait, cette expérience'
puts 'n\'a rien à voir avec la nourriture mexicaine.'
puts 'C\'est une expérience sur le fait de faire pipi au lit.'
puts 'La nourriture mexicaine était juste là pour vous'
puts 'prendre au dépourvu dans l\'espoir que vous répondriez'
puts 'plus honnêtement. Merci encore.'
puts
puts pipiAuLit
Un joli long programme, avec beaucoup de répétition. (Toutes les sections de code autour des questions sur la nourriture mexicaine sont identiques, et la question sur le pipi au lit est légèrement différente.) La répétition est une mauvaise chose. Mais nous ne pouvons pas faire un grand itérateur, car parfois nous voulons faire quelque chose entre les questions. Dans des situations comme celle-ci, il est préférable d'écrire une méthode. Voici comment :
def disMeuh
puts 'meuhhhhhhh...'
end
Euh... Notre programme n'a pas dit meuhhhhhhh... Pourquoi pas ? Parce que nous ne lui avons pas dit de le faire. Nous lui avons seulement dit comment faire meuhhhhhhh..., mais nous ne lui avons jamais dit de le faire réellement. Donnons-lui une autre chance :
def disMeuh
puts 'meuhhhhhhh...'
end
disMeuh
disMeuh
puts 'coin-coin'
disMeuh
disMeuh
Ah ! Beaucoup mieux. (Au cas où vous ne parleriez pas canard, c'était un canard au milieu du programme).
Donc, nous avons défini la méthode disMeuh. (Les noms de méthodes, comme les noms de variables, commencent par une lettre minuscule. Il y a des exceptions, comme + ou ==). Mais les méthodes ne doivent-elles pas toujours être associées à des objets ? Eh bien, oui. Et dans ce cas (tout comme avec puts et gets), la méthode est associée uniquement à l'objet représentant le programme dans son ensemble. Dans le prochain chapitre, nous verrons comment ajouter des méthodes à d'autres objets. Mais d'abord...
Paramètres de Méthodes
Vous avez peut-être remarqué que certaines méthodes (comme gets, ou to_s, ou reverse...) peuvent être appelées simplement sur un objet. Cependant, d'autres méthodes (comme +, -, puts...) prennent des paramètres pour dire à l'objet quoi faire avec la méthode. Par exemple, vous ne dites pas simplement 5+, n'est-ce pas ? Vous dites à 5 d'ajouter, mais vous ne lui dites pas quoi ajouter.
Pour ajouter un paramètre à disMeuh (le nombre de meuhs, par exemple), nous pouvons faire ceci :
def disMeuh nombreDeMeuhs
puts 'meuhhhhhhh...'*nombreDeMeuhs
end
disMeuh 3
puts 'oink-oink'
disMeuh # Cela donnera une erreur car aucun paramètre n'a été passé.
nombreDeMeuhs est une variable qui pointe vers le paramètre qui a été passé. Je vais le redire, mais c'est un peu déroutant : nombreDeMeuhs est une variable qui pointe vers le paramètre qui a été passé. Donc si je tape disMeuh 3, le paramètre est 3, et la variable nombreDeMeuhs pointe vers 3.
Comme vous pouvez le voir, maintenant le paramètre est requis. Après tout, ce que disMeuh est censé faire est de multiplier 'meuhhhhhhh' par un nombre. Mais par combien, si vous ne l'avez pas dit ? Votre ordinateur n'en a aucune idée.
Si nous comparons les objets en Ruby aux noms en français, les méthodes peuvent de la même manière être comparées aux verbes. Ainsi, vous pouvez penser aux paramètres comme des adverbes (comme dans disMeuh, où le paramètre nous dit comment faire disMeuh) ou parfois comme des compléments d'objet direct (comme dans puts, où le paramètre est ce que puts imprimera).
Variables Locales
Dans le programme suivant, il y a deux variables :
def doubler num
numFois2 = num*2
puts 'Le double de '+num.to_s+' est '+numFois2.to_s
end
doubler 44
Les variables sont num et numFois2. Les deux sont situées à l'intérieur de la méthode doubler. Celles-ci (et toutes les autres variables que vous avez vues jusqu'à présent) sont des variables locales. Cela signifie qu'elles vivent à l'intérieur de la méthode et ne peuvent pas en sortir. Si vous essayez, vous aurez une erreur :
def doubler num
numFois2 = num*2
puts 'Le double de '+num.to_s+' est '+numFois2.to_s
end
doubler 44
puts numFois2.to_s
Variable locale non définie... En fait, nous avons défini cette variable locale, mais elle n'est pas locale par rapport à l'endroit où nous avons essayé de l'utiliser ; elle est locale à la méthode.
Cela peut sembler peu pratique, mais c'est en fait très bien. Bien que vous n'ayez pas accès aux variables à l'intérieur des méthodes, cela signifie également que personne n'a accès à vos variables, et cela signifie que personne ne peut faire quelque chose comme ceci :
def petitePeste var
var = nil
puts 'HAHA ! J\'ai ruiné ta variable !'
end
var = 'Tu ne peux pas toucher à ma variable !'
petitePeste var
puts var
Il y a en fait deux variables dans ce petit programme nommées var : une à l'intérieur de la méthode petitePeste et une à l'extérieur. Lorsque vous avez appelé petitePeste var, nous avons vraiment juste passé la chaîne qui était dans var à l'autre, donc elles pointaient vers la même chaîne. Ensuite, la méthode petitePeste a pointé sa var locale vers nil, mais cela n'a rien fait à la var à l'extérieur de la méthode.
Valeurs de Retour
Vous avez peut-être remarqué que certaines méthodes renvoient quelque chose lorsque vous les appelez. Par exemple, la méthode gets renvoie une chaîne (la chaîne que vous avez tapée), et la méthode + dans 5+3 (qui est en fait 5.+(3)) renvoie 8. Les méthodes arithmétiques pour les nombres renvoient des nombres, et les méthodes arithmétiques pour les chaînes renvoient des chaînes.
Il est important de comprendre la différence entre les méthodes renvoyant une valeur à l'endroit où elle a été appelée, et votre programme générant une sortie sur votre écran, comme le fait puts. Notez que 5+3 renvoie 8 ; il n'imprime pas 8 à l'écran.
Alors que renvoie puts ? Nous ne nous en sommes jamais souciés avant, mais jetons un coup d'œil maintenant :
valeurRetour = puts 'Ce puts a renvoyé :'
puts valeurRetour
Le premier puts a renvoyé nil. Bien que nous n'ayons pas testé le deuxième puts, il a fait la même chose ; puts renvoie toujours nil. Chaque méthode doit renvoyer quelque chose, même si ce n'est que nil.
Faites une pause et écrivez un programme qui trouve ce que la méthode disMeuh a renvoyé.
Êtes-vous surpris ? Eh bien, voici comment cela fonctionne : la valeur de retour d'une méthode est simplement la dernière ligne évaluée dans la méthode. Dans le cas de la méthode disMeuh, cela signifie qu'elle a renvoyé 'puts meuhhhhhhh...'*nombreDeMeuhs, qui est juste nil, puisque puts renvoie toujours nil. Si nous voulions que toutes nos méthodes renvoient la chaîne 'yellow submarine', nous n'aurions qu'à la mettre à la fin :
def disMeuh nombreDeMeuhs
puts 'meuhhhhhhh...'*nombreDeMeuhs
'yellow submarine'
end
x = disMeuh 2
puts x
Maintenant, essayons à nouveau cette enquête de psychologie, mais cette fois nous allons écrire une méthode pour nous poser les questions. Elle devra prendre la question en paramètre et renvoyer true si la réponse était oui et false si la réponse était non. (Même si nous ignorons la réponse la plupart du temps, c'est une bonne idée de faire renvoyer la réponse par la méthode. De cette façon, nous pouvons l'utiliser pour la question sur le pipi au lit). Je vais aussi raccourcir la salutation et le débriefing, juste pour rendre la lecture plus facile :
def demander question
bonneReponse = false
while (not bonneReponse)
puts question
reponse = gets.chomp.downcase
if (reponse == 'oui' or reponse == 'non')
bonneReponse = true
if reponse == 'oui'
laReponse = true
else
laReponse = false
end
else
puts 'S\'il vous plaît, répondez par "oui" ou "non".'
end
end
laReponse # C'est ce que nous renvoyons (true ou false).
end
puts 'Bonjour, et merci pour...'
puts
demander 'Aimez-vous manger des tacos ?' # Nous ignorons cette valeur de retour.
demander 'Aimez-vous manger des burritos ?'
pipiAuLit = demander 'Faites-vous pipi au lit ?' # Nous sauvegardons cette valeur de retour.
demander 'Aimez-vous manger des chimichangas ?'
demander 'Aimez-vous manger des sopapillas ?'
demander 'Aimez-vous manger des tamales ?'
puts 'Juste quelques questions de plus...'
demander 'Aimez-vous boire de la horchata ?'
demander 'Aimez-vous manger des flautas ?'
puts
puts 'DÉBRIEFING :'
puts 'Merci pour...'
puts
puts pipiAuLit
Pas mal, hein ? Nous pouvons ajouter plus de questions (et ajouter plus de questions est facile maintenant), mais notre programme reste court ! C'est un énorme progrès — le rêve de tout programmeur paresseux.
Un Autre Grand Exemple
Je pense qu'un autre exemple de méthode serait très utile ici. Appelons celle-ci nombreFrancais. Cette méthode prendra un nombre, comme 22, et renverra la version française de celui-ci (dans ce cas, la chaîne 'vingt-deux'). Pour l'instant, limitons-le aux entiers entre 0 et 100.
(NOTE : Cette méthode utilise une nouvelle astuce pour revenir d'une méthode plus tôt en utilisant le mot-clé return, et introduit un nouveau concept : elsif. Cela devrait être clair dans le contexte).
def nombreFrancais nombre
# Nous ne voulons que des nombres entre 0 et 100.
if nombre < 0
return 'S\'il vous plaît, entrez un nombre qui n\'est pas négatif.'
end
if nombre > 100
return 'S\'il vous plaît, entrez un nombre qui est 100 ou moins.'
end
numChaine = '' # C'est la chaîne que nous allons renvoyer.
# "reste" est combien du nombre il nous reste à écrire.
# "ecriture" est la partie que nous écrivons en ce moment.
reste = nombre
ecriture = reste/100 # Combien de centaines reste-t-il à écrire ?
reste = reste - ecriture*100 # Soustrayez ces centaines.
if ecriture > 0
return 'cent'
end
ecriture = reste/10 # Combien de dizaines reste-t-il à écrire ?
reste = reste - ecriture*10 # Soustrayez ces dizaines.
if ecriture > 0
if ((ecriture == 1) and (reste > 0))
# Puisque nous ne pouvons pas écrire "dix-deux" au lieu de "douze",
# nous devons faire une exception spéciale pour ceux-ci.
if reste == 1
numChaine = numChaine + 'onze'
elsif reste == 2
numChaine = numChaine + 'douze'
elsif reste == 3
numChaine = numChaine + 'treize'
elsif reste == 4
numChaine = numChaine + 'quatorze'
elsif reste == 5
numChaine = numChaine + 'quinze'
elsif reste == 6
numChaine = numChaine + 'seize'
elsif reste == 7
numChaine = numChaine + 'dix-sept'
elsif reste == 8
numChaine = numChaine + 'dix-huit'
elsif reste == 9
numChaine = numChaine + 'dix-neuf'
end
# Puisque nous nous sommes déjà occupés du chiffre des unités,
# nous n'avons plus rien à écrire.
reste = 0
elsif ecriture == 1
numChaine = numChaine + 'dix'
elsif ecriture == 2
numChaine = numChaine + 'vingt'
elsif ecriture == 3
numChaine = numChaine + 'trente'
elsif ecriture == 4
numChaine = numChaine + 'quarante'
elsif ecriture == 5
numChaine = numChaine + 'cinquante'
elsif ecriture == 6
numChaine = numChaine + 'soixante'
elsif ecriture == 7
numChaine = numChaine + 'soixante-dix'
elsif ecriture == 8
numChaine = numChaine + 'quatre-vingt'
elsif ecriture == 9
numChaine = numChaine + 'quatre-vingt-dix'
end
if reste > 0
numChaine = numChaine + '-'
end
end
ecriture = reste # Combien d'unités reste-t-il à écrire ?
reste = 0 # Soustrayez ces unités.
if ecriture > 0
if ecriture == 1
numChaine = numChaine + 'un'
elsif ecriture == 2
numChaine = numChaine + 'deux'
elsif ecriture == 3
numChaine = numChaine + 'trois'
elsif ecriture == 4
numChaine = numChaine + 'quatre'
elsif ecriture == 5
numChaine = numChaine + 'cinq'
elsif ecriture == 6
numChaine = numChaine + 'six'
elsif ecriture == 7
numChaine = numChaine + 'sept'
elsif ecriture == 8
numChaine = numChaine + 'huit'
elsif ecriture == 9
numChaine = numChaine + 'neuf'
end
end
if numChaine == ''
# La seule façon dont "numChaine" pourrait être vide est si
# "nombre" est 0.
return 'zéro'
end
# Si nous sommes arrivés jusqu'ici, alors nous avions un nombre quelque part
# entre 0 et 100, donc nous devons renvoyer "numChaine".
numChaine
end
puts nombreFrancais( 0)
puts nombreFrancais( 9)
puts nombreFrancais( 10)
puts nombreFrancais( 11)
puts nombreFrancais( 17)
puts nombreFrancais( 32)
puts nombreFrancais( 88)
puts nombreFrancais( 99)
puts nombreFrancais(100)
Eh bien, il y a quelques choses que je n'aime pas dans ce programme. Premièrement : il y a trop de répétition. Deuxièmement : il ne gère pas les nombres supérieurs à 100. Troisièmement : il y a trop de cas spéciaux, trop de return. Utilisons des tableaux et essayons de nettoyer ça :
def nombreFrancais nombre
if nombre < 0 # Pas de nombres négatifs.
return 'S\'il vous plaît, entrez un nombre qui n\'est pas négatif.'
end
if nombre == 0
return 'zéro'
end
# Plus de cas spéciaux ! Plus de returns !
numChaine = '' # C'est la chaîne que nous allons renvoyer.
unites = ['un', 'deux', 'trois', 'quatre', 'cinq',
'six', 'sept', 'huit', 'neuf']
dizaines = ['dix', 'vingt', 'trente', 'quarante', 'cinquante',
'soixante', 'soixante-dix', 'quatre-vingt', 'quatre-vingt-dix']
adolescents = ['onze', 'douze', 'treize', 'quatorze', 'quinze',
'seize', 'dix-sept', 'dix-huit', 'dix-neuf']
# "reste" est combien du nombre il nous reste à écrire.
# "ecriture" est la partie que nous écrivons en ce moment.
reste = nombre
ecriture = reste/100 # Combien de centaines reste-t-il à écrire ?
reste = reste - ecriture*100 # Soustrayez ces centaines.
if ecriture > 0
# Maintenant, voici une astuce vraiment sournoise :
centaines = nombreFrancais ecriture
numChaine = numChaine + centaines + ' cents'
# Cela s'appelle la "récursivité". Alors qu'est-ce que je viens de faire ?
# J'ai dit à cette méthode de s'appeler elle-même, mais avec "ecriture" au lieu de
# "nombre". Rappelez-vous que "ecriture" est (pour le moment) le nombre de
# centaines que nous avons à écrire. Après avoir ajouté "centaines" à
# "numChaine", nous ajoutons la chaîne ' cents'. Donc, par exemple, si
# nous avons appelé à l'origine nombreFrancais avec 1999 (donc nombre = 1999),
# alors à ce stade, ecriture serait 19, et "reste" serait 99.
# La chose la plus paresseuse à faire à ce stade est de faire écrire
# 'dix-neuf' par nombreFrancais pour nous, puis nous écrivons ' cents',
# et ensuite le reste de nombreFrancais écrit 'quatre-vingt-dix-neuf'.
if reste > 0
# Pour ne pas écrire 'deux centcinquante et un'...
numChaine = numChaine + ' '
end
end
ecriture = reste/10 # Combien de dizaines reste-t-il à écrire ?
reste = reste - ecriture*10 # Soustrayez ces dizaines.
if ecriture > 0
if ((ecriture == 1) and (reste > 0))
# Puisque nous ne pouvons pas écrire "dix-deux" au lieu de "douze",
# nous devons faire une exception spéciale pour ceux-ci.
numChaine = numChaine + adolescents[reste-1]
# Le "-1" est parce que adolescents[3] est 'quatorze', pas 'treize'.
# Puisque nous nous sommes déjà occupés du chiffre des unités,
# nous n'avons plus rien à écrire.
reste = 0
else
numChaine = numChaine + dizaines[ecriture-1]
# Le "-1" est parce que dizaines[3] est 'quarante', pas 'trente'.
end
if reste > 0
# Pour ne pas écrire 'soixantequatre'...
numChaine = numChaine + '-'
end
end
ecriture = reste # Combien d'unités reste-t-il à écrire ?
reste = 0 # Soustrayez ces unités.
if ecriture > 0
numChaine = numChaine + unites[ecriture-1]
# Le "-1" est parce que unites[3] est 'quatre', pas 'trois'.
end
# Maintenant, nous renvoyons simplement "numChaine"...
numChaine
end
puts nombreFrancais( 0)
puts nombreFrancais( 9)
puts nombreFrancais( 10)
puts nombreFrancais( 11)
puts nombreFrancais( 17)
puts nombreFrancais( 32)
puts nombreFrancais( 88)
puts nombreFrancais( 99)
puts nombreFrancais(100)
puts nombreFrancais(101)
puts nombreFrancais(234)
puts nombreFrancais(3211)
puts nombreFrancais(999999)
puts nombreFrancais(1000000000000)
Ahhhh.... C'est beaucoup mieux. Le programme est assez dense, c'est pourquoi j'ai mis tant de commentaires. Il fonctionne même pour les grands nombres... bien que pas tout à fait aussi joliment qu'on pourrait l'espérer. Par exemple, je pense que 'un billion' serait une valeur de retour plus agréable pour ce dernier nombre, ou même 'un million de millions'. En fait, vous pouvez faire ça dès maintenant...
Quelques Choses à Essayer
- Améliorez
nombreFrancais. D'abord, mettez les milliers. Donc il devrait renvoyer 'mille' au lieu de 'dix cents' et 'dix mille' au lieu de 'un cents cents'. - Développez
nombreFrancaisun peu plus. Maintenant, mettez les millions, pour obtenir 'un million' au lieu de 'un mille mille'. Ensuite, essayez d'ajouter les milliards et les billions. Jusqu'où pouvez-vous aller ? - "Quatre-vingt-dix-neuf bouteilles de bière..." En utilisant
nombreFrancaiset votre ancien programme, écrivez les paroles de cette chanson de la bonne façon cette fois. Punissez votre ordinateur : faites-le commencer à 9999. (Ne choisissez pas un nombre trop grand, cependant, car écrire tout cela à l'écran prend un certain temps à votre ordinateur. Cent mille bouteilles de bière prennent un certain temps ; et si vous choisissez un million, vous vous punirez aussi !)
Félicitations ! À ce stade, vous êtes vraiment un programmeur ! Vous avez appris tout ce dont vous avez besoin pour écrire d'énormes programmes à partir de zéro. Si vous avez des idées de programmes que vous aimeriez écrire pour vous-même, lancez-vous !
Bien sûr, tout construire à partir de zéro peut être un processus lent. Pourquoi passer du temps à écrire du code que quelqu'un d'autre a déjà écrit ? Vous voulez envoyer un e-mail ? Vous voulez enregistrer et charger des fichiers sur votre ordinateur ? Que diriez-vous de générer des pages Web pour un tutoriel où les exemples de code sont réellement exécutés à chaque chargement de la page ? :) Ruby a de nombreux types d'objets différents que nous pouvons utiliser pour nous aider à écrire des programmes mieux et plus rapidement.