• Aucun résultat trouvé

[PDF] Introduction en ce qui concerne langage LUA | Cours informatique

N/A
N/A
Protected

Academic year: 2021

Partager "[PDF] Introduction en ce qui concerne langage LUA | Cours informatique"

Copied!
27
0
0

Texte intégral

(1)

Tables et fonctions

Nous allons, dans ce chapitre étudier plus en détail les tables et les fonctions que nous avons entrevues dans les chapitres précédents. Une table, c’est pour ainsi dire, une supervariable qui a la faculté de contenir plusieurs objets, au lieu d'un seul, comme les autres variables. La table est, pour ainsi dire, le point fort du Lua par rapport aux autres langages à cause des possibilités offertes qui sont plus nombreuses que pour les autres langages. En contrepartie, cette notion est plus dure à assimiler pour le Lua que pour les autres langages. Nous allons donc procéder par étapes, dans ce chapitre, en

commençant par les notions simples et en compliquant au fur et à mesure. Le lecteur n’est pas obligé de tout assimiler. Comprendre les premières notions lui permettra de traiter les tables en Lua comme elles sont traitées dans les autres langages et cela peut lui être suffisant pour programmer correctement. Nous approfondirons aussi les

fonctions. Si nous étudions ces deux notions dans un même chapitre, c’est parce-qu’il y a une certaine interconnexion entre tables et fonctions. En effet, on peut faire des tables de fonctions et les fonctions peuvent recevoir des tables en arguments. Il est donc difficile de décider quoi étudier en premier !

Définition élémentaire d'une table

Au premier chapitre, nous avons effleuré la définition d'une table lorsque nous avons étudier l'instruction

local p = {}

Mais une table ne s’appelle pas forcément p. On peut lui donner un nom plus parlant sur sa fonction. Par exemple :

local semaine = {}

Et il est possible de l'initialiser :

local semaine = {"lundi", "mardi", "mercredi", "jeudi", "vendredi",

"samedi", "dimanche"}

On aurait pu, tout aussi bien, créer une table de nombres. Par exemple :

local nombres_premiers = {2,3,5,7,11,13,17,19,23,29,31,37,41}

Comment accéder à un élément d'une table. Il suffit d'écrire le nom de la table suivit de la position entre crochet de l’objet auquel on souhaite accéder. Ce genre de table, indexée par la position de l'objet, s’appelle une séquence en Lua.

Par exemple, pour notre première table, semaine[3] représente "mercredi". C'est le troisième jour de la semaine.

(2)

Pour notre deuxième table ; nombres_premiers[5] représente le cinquième nombre premier qui est 11.

Prenons un exemple pour illustrer ce que l’on vient de dire :

Écrivons un programme qui traduit un jour de la semaine en anglais. On l'a déjà fait au premier chapitre, mais cette fois écrivons un programme qui à partir seulement d'un nombre, nous donne une phrase indiquant la traduction du jour de position le nombre donné. Par exemple si l’on rentre 4, on doit obtenir la phrase : "La traduction de jeudi, en anglais est thursday" car jeudi est le quatrième jour de la semaine.

Dans le Module:Traduit écrivons :

local p = {}

local semaine = {"lundi", "mardi", "mercredi", "jeudi", "vendredi",

"samedi", "dimanche"}

local week = {"monday", "tuesday", "wednesday", "thursday", "friday",

"saturday", "sunday"}

function p.baratin1(frame)

index = tonumber(frame.args[1])

return "La traduction de "..semaine[index]..", en anglais, est "..week[index]

end

return p

En écrivant : {{#invoke:Traduit|baratin1|5}}, nous obtenons : La traduction de vendredi, en anglais, est friday

Index évolués

Nous venons de voir que les index des tables peuvent s'écrire à l'aide de nombres. Mais il est possible aussi de les écrire à l'aide de chaîne de caractères. Reprenons

notre Module:Traduit. Nous pouvons alors imaginer un moyen astucieux de traduire un mot en anglais en se servant de ce mot directement comme index d'une table qui contiendrait toutes les traductions. Par exemple, dans une table tab avec l'index "chien", on accéderait à l'emplacement tab["chien"] où se trouverait la chaîne de caractère dog Sur ce principe, écrivons donc un nouveau programme de traduction des jours de la semaine en anglais :

local p = {}

local sem = { ["lundi"] = "monday", ["mardi"] = "tuesday",

["mercredi"] = "wednesday", ["jeudi"] = "thursday", ["vendredi"] =

"friday", ["samedi"] = "saturday", ["dimanche"] = "sunday"}

function p.anglais(frame)

(3)

return "La traduction de "..jour..", en anglais, est "..sem[jour]

end

return p

En écrivant : {{#invoke:Traduit|anglais|samedi}}, nous obtenons : La traduction de samedi, en anglais, est saturday

Nous remarquons comment la table sem a été pré-rempli. Nous sommes obligé de bien préciser à quel indice correspond quelle information.

Si, dans un programme, nous sommes emmené à accéder directement à une donnée de la table sans passer par le biais d'une variable, nous pouvons l'écrire autrement. Par exemple, au lieu d'écrire sem["mardi"], on peut écrire, plus simplement, sem.mardi. Pour bien vérifier l'équivalence de deux notations, nous avons rajouté une fonction dans le Module:Traduit :

function p.information(frame)

return "Le premier jour de la semaine anglaise est "..sem["lundi"].." et le dernier jour est "..sem.dimanche

end

En écrivant : {{#invoke:Traduit|information}}, nous obtenons : Le premier jour de la semaine anglaise est monday et le dernier jour est sunday

Nous avons utilisé les deux notations dans la même phrase pour vérifier qu’elles sont bien équivalente.

Par contre, si dans la fonction p.anglais, nous avions écrit sem.jour au lieu de sem[jour], nous aurions une erreur de script car jour est une variable qui contient une chaîne de caractères, mais n'est pas, elle-même, une chaîne de caractères.

Question : Lorsque les index sont des nombres, ne peut-on pas accéder à

un élément de la table en écrivant sem.3 au lieu de sem[3].

Réponse : Non, car sem.3 est équivalent à sem["3"], mais pas à sem[3].

La notation que l’on vient de voir se répercute aussi sur la déclaration des tables. C'est-à-dire que la table sem de notre Module:Traduit aurait tout aussi bien pu s'écrire sous la forme simplifiée suivante :

local p = {}

local sem = { lundi = "monday", mardi = "tuesday", mercredi =

"wednesday", jeudi = "thursday", vendredi = "friday", samedi =

"saturday", dimanche = "sunday"}

function p.anglais(frame)

(4)

return "La traduction de "..jour..", en anglais, est "..sem[jour]

end

return p

Nous voyons, par exemple, que ["lundi"] a été remplacé par lundi.

Ce que nous avons dit n’est pas tout à fait vrai. Si nous sommes obligé d'utiliser, comme index, une chaîne de caractères ayant un accent alors les deux notations ne sont plus équivalentes. Par exemple sem["Nénuphar"] ne peut pas être remplacé par sem.Nénuphar qui sera rejeté par l'éditeur. Il en est de même pour la déclaration des tableaux. Ci-dessus nous avons eu de la chance car aucun des jours de la semaine ne prend d'accent.

Tables de tables

On peut aussi manipuler des tables de tables, c'est-à-dire une table contenant des tables.

Si, par exemple, on veut déclarer une table contenant quatre tables, on écrira :

local t = {{},{},{},{}}

Mathématiquement, ce genre de table peut correspondre à des matrices. Par exemple, la matrice :

se déclarera :

local A = {{2,1,-4,6},{5,-3,-2,4},{1,3,-4,7},{-5,3,2,5}}

Si l’on veut accéder au nombre -2 se trouvant à la deuxième ligne, troisième colonne, on écrira A[2][3].

Si l’on déclare une table (ou matrice) B ainsi : local B = {}

Et que l’on écrit dans le programme, par exemple :

B[3][1] = 9

(5)

Fonctions relatives aux tables

Dans le chapitre 8, nous étudierons plus en détail d'autres fonctions relatives aux tables, en particulier les fonctions :

 table.insert(t, ligne) : incrémente la table avec "ligne".  table.remove(t,ligne) : retire un élément de la table.

 table.concat(t, séparateur) : convertit la table en une chaîne de caractère, en séparant chaque ligne par une éventuelle autre chaîne.

 table.maxn(t) : Retourne le plus grand index numérique positif utilisé dans la table.

 table.sort(t) : Permet de trier la table.  table.getn(t) : renvoie la taille de la table.

Complément sur les fonctions

Fonctions appelées par une fonction

Nous avons déjà bien étudié les fonctions. Mais toutes les fonctions que nous avons vu jusqu'à maintenant étaient placé d'office dans une table que nous avons appelé p pour les besoins de #invoke (Ce qui nous montre déjà que l’on peut faire des tables de fonctions). Nous allons voir maintenant qu'une fonction peut exister sans être dans une table. Une fonction peut être appelée simplement par une autre fonction. Prenons un exemple :

Dans le Module:Fonction, écrivons une fonction qui calcule automatiquement les carrés des 4 premiers nombres premiers en prenant soin de mettre à part la fonction qui élève au carré.

local p = {}

function f(x)

return x^2

end

function p.carre1(frame)

local reponse = "<u>Nombres premiers élevés aux carrés</u> <br />"

reponse = reponse.."Le carré du nombre 2 est "..f(2).."<br />"

reponse = reponse.."Le carré du nombre 3 est "..f(3).."<br />"

reponse = reponse.."Le carré du nombre 5 est "..f(5).."<br />"

reponse = reponse.."Le carré du nombre 7 est "..f(7).."<br />"

(6)

end

return p

La fonction f se contente d'élever au carré le nombre x, qui représente son argument, et nous voyons que cette fonction est appelée dans la fonction p.carre1 qui se trouve dans la table p.

En écrivant : {{#invoke:Fonction|carre1}}, nous obtenons : Nombres premiers élevés aux carrés

Le carré du nombre 2 est 4 Le carré du nombre 3 est 9 Le carré du nombre 5 est 25 Le carré du nombre 7 est 49

Paramètres et valeurs retournées par une fonction

Les fonctions peuvent recevoir plusieurs paramètres. Pour passer plusieurs paramètres à une fonction, il suffit de les écrire simplement en les séparant par des virgules. Par exemple :

function f(x,y,z)

return x^2+y^2+z^2

end

Plus remarquable encore, une fonction peut retourner plusieurs valeurs en les séparant par des virgules :

function f(x)

return x^2,2x,5x-3

end

Nous voyons que la forme que prennent les valeurs en sortant est similaire à la forme que prennent les paramètres à l'entrée. Nous pouvons mettre ceci à profit en emboîtant des fonctions (fonctions composées). Prenons un exemple pour voir si cela marche bien : local p = {} function g(x,y,z) return 2*x+y+3*z end function h(x) return x,2*x,x end

(7)

function p.composition(frame)

return g(h(frame.args[1]))

end

return p

Nous voyons que la fonction h a un seul paramètre mais retourne trois valeurs. La fonction g, qui traite trois paramètres, peut recevoir les trois valeurs retournées par h et nous renvoie une valeur. Nous pouvons vérifier que ça marche bien avec les deux exemples suivants :

En écrivant : {{#invoke:Fonction|composition|5}}, nous obtenons : 35 En écrivant : {{#invoke:Fonction|composition|3}}, nous obtenons : 21

Il nous reste tout de même un petit problème : Commet récupérer les valeurs retournées par une fonction qui retourne plusieurs valeurs. C'est là que nous allons utiliser

l'affectation simultanée de plusieurs variables que nous avons entrevu à la fin du premier chapitre. Supposons que f soit une fonction qui retourne trois valeurs par exemple et que nous voulions récupérer les valeurs retournées pour f(3) par exemple. Nous écrirons tout simplement:

a,b,c = f(3)

et les trois valeurs retournées par f seront respectivement dans les trois variables a, b, c. Une question vient à l'esprit : Et si nous ne sommes intéressés que par la première et la troisième valeur ? Dans ce cas nous écrirons :

a,,c = f(3)

Et si nous ne sommes intéressé que par la première valeur ? Dans ce cas, nous écrirons :

a = f(3)

Et si nous ne sommes intéressé que par la troisième valeur ? Dans ce cas, nous écrirons :

,,c = f(3)

Et si nous ne sommes intéressé que par les deux premières valeurs ? Dans ce cas, nous écrirons :

a,b = f(3)

(8)

Fonctions récursives

En Lua, comme en C ou en Pascal (mais pas en Fortran, ni en Cobol), les fonctions peuvent s'appeler elles-mêmes. On appelle ce phénomène la récursivité. Prenons l'exemple classique de la fonction factorielle.

local p = {}

function fact(n) if n == 0 then

return 1 -- on renvoie la valeur 1 quand le paramètre vaut 0

else

return n * fact(n - 1) end

end

function p.factorielle(frame)

return fact(frame.args[1])

end

return p

La fonction récursive est la fonction fact qui a pour argument n et qui fait appel à fact(n-1) si n est différent de 0. Cette fonction va s'appeler elle-même jusqu'à ce que son argument soit nul. Par exemple, si l’on veut calculer factorielle de 4 que l’on note 4!, on aura, en suivant le cheminement de la fonction :

4! = 4✕3! = 4✕3✕2! = 4✕3✕2✕1! = 4✕3✕2✕1✕0! = 4✕3✕2✕1✕1 = 4✕3✕2✕1 = 24 En écrivant : {{#invoke:Calcul|factorielle|5}}, nous obtenons : 120 car 120 =

5✕4✕3✕2✕1

Question : Dans le programme précédent, n'aurait-on pas pu rendre

directement la fonction p.factorielle récurssive au lieu de lui faire appeler une autre fonction récursive, ici la fonction fact ?

Réponse : Non, car la fonction p.factorielle a pour argument frame et est

donc, par conséquent, dédiée à recevoir des informations de l'extérieur du module et pas de l'intérieur. Elle n'est donc pas appelable de l'intérieur du module et donc ne peut pas s'appeler elle-même.

Tables de fonctions

En lua, nous pouvons créer des tables de fonctions. Nous devrions commencer à y être habitués car depuis le début de cette leçon, nous utilisons une table que nous avons appelé p (mais qui pourrait s'appeler autrement) dans laquelle, nous rangeons nos fonctions. Dans ce paragraphe, nous allons essayer de bien clarifier cette notion que nous utilisons, peut-être, mécaniquement sans trop bien comprendre ce que nous faisons. Pour cela nous allons utiliser des exemples.

(9)

Nous attirons l'attention du lecteur sur le fait que les exemples qui suivent (comme la plupart des exemples de cette leçon) pourront paraître totalement loufoques au programmeur de formation. Ils ont uniquement pour but de bien faire assimiler la notions de tables de fonctions aux étudiants et n'ont, par contre, aucune valeur d'exemple sur la manière de résoudre un problème concret.

Exemple1

Nous allons écrire dans un Module:Ajout, trois fonctions f,g,h ayant pour but d'ajouter respectivement 1, 2, 3 à son argument. Nous écrirons ensuite une fonction p.ajoute qui, dans un premier temps, va ranger les trois fonctions f, g, h dans une table et ensuite, dans un deuxième temps, va incrémenter son premier argument, de la valeur indiqué par son deuxième argument, en utilisant les fonctions rangées dans la table :

local p = {} function f(x) return x+1 end function g(x) return x+2 end function h(x) return x+3 end

function p.ajoute(frame)

local Aj = {}

local reponse = tonumber(frame.args[1])

Aj.ajoute1 = f

Aj.ajoute2 = g

Aj.ajoute3 = h

if frame.args[2] == "1" then reponse = Aj.ajoute1(reponse)

end

if frame.args[2] == "2" then reponse = Aj.ajoute2(reponse)

end

if frame.args[2] == "3" then reponse = Aj.ajoute3(reponse)

end

return reponse

end

return p

En écrivant : {{#invoke:Ajout|ajoute|17|2}}, nous obtenons : 19 Exemple2

(10)

Nous avons vu, au début de ce chapitre, que la notation Aj.ajoute1 utilisée pour les tables était une façon de noter, plus simplement, un accès à une table indexée par des chaînes de caractères et qui se noterait plus logiquement Aj["ajoute1"]. Nous allons donc, à titre d'exemple 2, rajouter, dans le Module:Ajout, une fonction p.rajoute qui est l'exacte réplique de la fonction p.ajoute mais utilisant l'autre notation pour l'accès a la table de fonctions Aj

function p.rajoute(frame)

local Aj = {}

local reponse = tonumber(frame.args[1]) Aj["ajoute1"] = f

Aj["ajoute2"] = g Aj["ajoute3"] = h

if frame.args[2] == "1" then reponse = Aj["ajoute1"](reponse)

end

if frame.args[2] == "2" then reponse = Aj["ajoute2"](reponse)

end

if frame.args[2] == "3" then reponse = Aj["ajoute3"](reponse)

end

return reponse

end

En écrivant : {{#invoke:Ajout|rajoute|23|1}}, nous obtenons : 24 Nous voyons donc que la notation Aj.ajoute1 est bien équivalente à la notation Aj["ajoute1"]

Pourquoi avons nous pris la peine de donner ces deux exemples identiques, à la notation de l'accès à la table près. C'est pour que l'étudiant prenne bien conscience que, dans la notation Aj.ajoute1, nous n'avons pas une fonction qui s'appellerait ajoute1 et qui serait placée dans la table Aj (comme certains pourraient le croire) mais nous avons une table de fonctions indexée par des chaînes de caractères. ajoute1 représente la chaîne de caractère "ajoute1" qui sert d'index d'accès à une fonction se trouvant dans la table Aj. À l'appui de ce que nous venons de dire, on pourrait souligner le fait que

puisque ajoute1 représente une chaîne de caractères et pas une fonction, nous aurions pu créer à part une vraie fonction ajoute1 et il n'y aurait pas eu de conflit. C'est ce que l’on aurait pu faire un peu plus haut lorsque nous avons donné l'exemple de la fonction factorielle comme fonction récursive. Nous avions écrit pour éviter d'embrouiller les esprit :

local p = {}

function fact(n) if n == 0 then

return 1 -- on renvoie la valeur 1 quand le paramètre vaut 0

else

return n * fact(n - 1) end

(11)

end

function p.factorielle(frame)

return fact(frame.args[1])

end

return p

Mais nous aurions pu écrire :

local p = {}

function factorielle(n) if n == 0 then

return 1 -- on renvoie la valeur 1 quand le paramètre vaut 0

else

return n * factorielle(n - 1)

end end

function p.factorielle(frame)

return fact(frame.args[1])

end

return p

et cela aurait tout aussi bien fonctionné. Exemple3

La notation Aj.ajoute1 semble plus simple que la notation Aj["ajoute1"]. Nous allons donc dans cet exemple 3, mettre en évidence un intérêt, que peut avoir la dernière notation, en simplifiant l'exemple 2.

Dans le Module:Ajout, nous rajouterons la fonction p.incremente qui est une

simplification de la fonction p.rajoute mettant à profil le fait que l’on a accès à l'index sous forme de chaîne de caractères. Nous écrirons :

function p.incremente(frame)

local Aj = {}

local index = "ajoute"..frame.args[2]

local reponse = tonumber(frame.args[1]) Aj["ajoute1"] = f

Aj["ajoute2"] = g Aj["ajoute3"] = h

(12)

return reponse

end

En écrivant : {{#invoke:Ajout|incremente|47|3}}, nous obtenons : 50

Fonctions ayant une table pour argument

Une fonction peut recevoir en argument tout type d'objet. Par conséquent, une fonction peut recevoir une table en argument. Il y a toutefois une petite différence dans le passage d'une table en argument dans une fonction. Les tables sont passés par

référence alors que la plupart des autres variables sont passées par valeur. Nous allons nous efforcer de bien comprendre ce que cela signifie dans la suite de ce paragraphe. Lorsqu'on passe un objet par valeur à une fonction, cela signifie que la fonction recopie l’objet dans la variable déclarée en argument dans la fonction. par exemple reprenons la fonction f défini plus haut :

function f(x)

return x^2

end

Si dans une autre fonction, on écrit

resultat = 7+f(a)

À l'appel de la fonction f par f(a), la valeur de a est recopié dans la variable x définie dans la fonction f et c’est x qui va être élevée au carré et pas la variable a.

Par contre, lorsqu'on passe un objet par référence à une fonction, cela signifie que la fonction reçoit l'adresse de l’objet qui lui est passé et pas sa valeur. Par conséquent la fonction va agir directement sur l’objet du programme appelant et pas sur une recopie de l'objet.

Dans le Lua, les tables sont des variables passées par référence. Cela peut se comprendre dans la mesure où les tables peuvent être des objets énormes puisque pouvant contenir un grand nombre d'objets. Si l’on devait recopier la table dans la fonction à chaque appel de fonction, la perte de temps ainsi que l'occupation mémoire serait trop importante.

Nous allons prendre deux exemples pour mettre en évidence la différence entre le passage par valeur et le passage par référence :

Dans un Module:Passage, nous écrirons deux fonctions p.valeur et p.reference. La première, appelant une fonction val auquel elle passe une variable par valeur (ici un nombre). La deuxième appelant une autre fonction ref auquel elle passe une variable par référence (ici une table). Chacune des deux fonctions va ensuite modifier l’objet passé. Nous vérifierons ensuite, dans le programme appelant, si la modification s'est aussi répercuté sur l’objet passé :

(13)

function val(x) -- x est sensé être un nombre

x = x + 3 -- On essaye d'incrémenté de 3 le contenu de x

end

function ref(x) -- x est sensé être une table

x[1] = x[1] + 3 -- On essaye d'incrémenté de 3 la première

valeur de la table end

function p.valeur(frame)

local a = tonumber(frame.args[1]) -- a est déclaré comme nombre et est initialisé avec la valeur de l'argument

val(a) -- appel de la fonction, ici a contient un nombre

return a -- On retourne le contenu de a pour voir s'il a été modifié

end

function p.reference(frame)

local a = {tonumber(frame.args[1])} -- a est déclaré comme table et est initialisé avec la valeur de l'argument en a[1]

ref(a) -- appel de la fonction, ici a contient une table

return a[1] -- On retourne le contenu de a pour voir s'il a été modifié

end

return p

En tapant {{#invoke:Passage|valeur|37}}, nous obtenons : 37. Nous voyons que l'argument n'a pas été modifié.

En tapant {{#invoke:Passage|reference|37}}, nous obtenons : 40. Nous voyons que l'argument a été incrémenté de 3.

Visibilité d'une variable

Nous avons, jusqu'à présent toujours déclaré les variables locale en début de fonction ou en début de bloc. La question qui se pose ici est de savoir ce qui se passe si la variable n’est pas déclarée localement en début de bloc, mais au milieu par exemple. Que les utilisateurs soient d'ores et déjà rassurés, cela ne provoque pas une erreur de script. En fait, une variable ne pourra être utilisée localement qu’à partir du moment où elle est déclarée comme locale. Si on tente d’utiliser ou de modifier une variable avant de la déclarer, on utilisera ou l’on modifiera une autre variable de même nom, celle-ci étant globale ou éventuellement déclarée locale en début de module.

(14)

Les structures de contrôle, dont la plupart sont appelées « boucles », permettent :  soit de prendre une décision en fonction du contexte ;

 soit de répéter une opération sur des objets différents ;

 soit de répéter une opération tant que quelque chose est vrai ou jusqu'à ce que quelque chose soit réalisé.

La structure if..then..else

Nous avons déjà étudié cette structure au premier chapitre car il est difficile de s'en passer puisqu'elle permet de prendre une décision en fonction d'une condition. Nous rappelons que, en français la

structure if condition then instruction1 else instruction2 end signifie : Si une condition est remplie exécuter instruction1 sinon exécuter instruction2. Dans le Module:Balance, nous avons donné l'exemple suivant :

local p = {}

function p.alerte3(frame)

local poids = tonumber(frame.args[1])

local reponse

if poids < 55 then

reponse = "Votre poids est acceptable" else

reponse = "Attention, vous commencez à grossir !" end

return reponse

end

return p

Dans une autre page, si nous écrivons {{#invoke:Balance|alerte3|57}}, nous obtenons : Attention, vous commencez à grossir !

Nous rajouterons, dans ce chapitre, qu’il est possible aussi d'emboîter les

boucles if..then..else. Par exemple, toujours dans le Module:Balance, nous avons écrit une quatrième fonction p.alerte4 ainsi :

local p = {}

function p.alerte4(frame)

local poids = tonumber(frame.args[1])

local reponse

if poids < 55 then

reponse = "Votre poids est acceptable" else

if poids < 60 then

reponse = "Attention, vous commencez à grossir

(15)

else

reponse = "Grosse vache !!" end

end

return reponse

end

return p

Qui présente l'avantage de fournir à l'utilisatrice une information plus complète[1].

Si l’on souhaite emboîter un très grand nombre de structures if..then..else, on peut le faire plus simplement en utilisant l'instruction elseif. Voir la fonction exemple,

ci-dessous, qui nous confirme le nombre, rentré en argument, si celui-ci est compris entre 1 et 5 ou répond « Je ne sais pas » dans les autres cas :

local p = {}

function p.exemple(frame)

local n = tonumber(frame.args[1])

if n == 1 then

return "Le nombre est un" elseif n == 2 then

return "Le nombre est deux" elseif n == 3 then

return "Le nombre est trois" elseif n == 4 then

return "Le nombre est quatre" elseif n == 5 then

return "Le nombre est cinq" else

return "Je ne sais pas" end

end

return p

La structure while..do

La structure while..do permet de répéter un ensemble d'instructions tant qu'une

condition est vraie. Sa syntaxe générale est : while condition do instructions end, ce qui signifie, en français : Tant que condition faire instructions fin.

Prenons un exemple : écrivons un programme qui va écrire le plus petit nombre,

supérieur à un nombre donné, s'écrivant comme somme des premiers nombres entiers. Par exemple le plus petit nombre, supérieur à 13, vérifiant cette condition, est 15 car 1 + 2 + 3 + 4 = 10 et 1 + 2 + 3 + 4 + 5 = 15. Dans le Module:Calcul nous écrirons :

(16)

local p = {}

function p.somme1(frame)

local limite = tonumber(frame.args[1])

local reponse = 0

local entier = 1

while reponse < limite do

reponse = reponse + entier

entier = entier + 1

end

return reponse

end

return p

Dans une autre page, si nous écrivons {{#invoke:Calcul|somme1|13}}, nous obtenons : 15

Dans cet exemple, nous voyons que la variable limite a pris la valeur 13. Et tant

que reponse avait une valeur inférieure à 13, reponse a été incrémentée de la valeur se trouvant dans entier. Comme entier valait 1 au départ et a été incrémentée de 1 dans chaque passage dans la boucle, nous voyons que reponse a pris la valeur 1 au premier passage dans la boucle, puis a pris la valeur 1 + 2 = 3 au deuxième passage, puis la valeur 1 + 2 + 3 = 6 au troisième passage, puis 1 + 2 + 3 + 4 = 10 au quatrième passage, puis 1 + 2 + 3 + 4 + 5 = 15 au cinquième passage. À ce moment-là, la condition reponse

< limite est devenu fausse et par conséquent nous sommes sorti de la boucle

avec reponse contenant la valeur 15.

La structure repeat..until

[

modifier

|

modifier le wikicode

]

La structure : repeat..until permet de répéter un ensemble d'instructions jusqu'à ce qu'une condition soit vraie. Sa syntaxe générale

est : repeat instructions until condition, ce qui signifie, en français : Répéter instructions jusqu'à condition.

Si nous essayons de réaliser une fonction réalisant la même chose qu'au paragraphe précédent, nous obtiendrons :

local p = {}

function p.somme2(frame)

local limite = tonumber(frame.args[1])

local reponse = 0

local entier = 1

repeat

reponse = reponse + entier

entier = entier + 1

until reponse > limite

(17)

end

return p

Dans une autre page, si nous écrivons {{#invoke:Calcul|somme2|13}}, nous obtenons : 15

Dans ce nouvel exemple, nous voyons que la variable limite a pris la valeur

13. reponse a été incrémentée de la valeur se trouvant dans entier qui, comme dans l'exemple précédent, est incrémentée de 1 à chaque passage dans la boucle, ces deux opérations se renouvelant jusqu'à ce que reponse prenne une valeur supérieure à 13. Nous voyons que reponse a pris la valeur 1 au premier passage dans la boucle, puis a pris la valeur 1 + 2 = 3 au deuxième passage, puis la valeur 1 + 2 + 3 = 6 au troisième passage, puis 1 + 2 + 3 + 4 = 10 au quatrième passage, puis 1 + 2 + 3 + 4 + 5 = 15 au cinquième passage. À ce moment-là, la condition reponse > limite est devenue vraie et, par conséquent, nous sommes sorti de la boucle avec reponse contenant la valeur 15. Ce qui différencie ce deuxième exemple du premier, c’est que l'exécution de la boucle aura lieu au moins une fois et reponse prendra donc au minimum la valeur 1. Si nous tapons {{#invoke:Calcul|somme2|0}}, nous obtiendrons 1 alors que dans l'exemple précédent {{#invoke:Calcul|somme1|0}} aurait donné 0.

Nous pouvons dire que, dans ce cas, l'exemple précédent est meilleur puisque donnant une réponse juste pour la valeur 0.

En général, tout ce qui est réalisable avec la structure while..do est aussi réalisable avec la structure repeat..until. La principale différence réside dans le fait que, dans la

structure while..do, la condition est testée avant chaque exécution des instructions et dans la structure repeat..until, la condition est testé après chaque exécution des instructions. Ce qui, selon ce que l’on veut faire, va déterminer le choix entre les deux structures.

La structure for..do

Nous avons vu, dans les deux paragraphes précédents, des structures où le nombre de passage dans la boucle n’est pas connu d'avance et dépend d'une condition qui doit rester vraie ou qui doit devenir vraie. Si l’on veut répéter une opération un certain nombre de fois bien précis connu avant l'entrée dans la boucle, on utilisera la structure for..do. La seule petite différence entre chaque exécution sera déterminée par une variable d'index qui pour chaque exécution prendra une valeur différente et qui pourra être éventuellement utilisée dans le corps de la boucle.

Dans le Lua, il existe deux formes de la boucle for.

Première forme de la boucle for

La première forme de la boucle for se rédige sous la forme :

for index = m, n, p do

instructions

end

m, n, p étant trois entiers. p est facultatif. Pour chaque passage dans la boucle, la variable index prendra toutes les valeurs de m à n en étant incrémenté, chaque fois, de la valeurs de p.

(18)

Prenons des exemples.

Écrivons, toujours dans notre Module:Calcul, une fonction echo qui répétera un mot un certain nombre de fois, lui aussi entré en paramètre. Nous écrirons :

local p = {}

function p.echo(frame)

local nom = frame.args[1]

local occurence = tonumber(frame.args[2])

local reponse = " " for i = 1, occurence do

reponse = reponse.." "..nom

end

return reponse

end

return p

Dans une autre page, si nous écrivons {{#invoke:Calcul|echo|plouf|5}}, nous obtenons : plouf plouf plouf plouf plouf

Voyons, plus en détail, comment fonctionne cette boucle. Dans notre

exemple, occurence prend la valeur 5. Par conséquent, notre boucle devient :

for i = 1, 5 do

reponse = reponse.." "..nom

end

i = 1, 5 signifie que i doit prendre toutes les valeurs de 1 à 5 et la boucle s'exécutera pour chacune de ces valeurs. Nous aurons donc obligatoirement 5 passages dans la boucle. En français, on pourrait traduire cette exécution par : Pour i prenant toutes les valeurs

de 1 à 5 faire instructions.

On peut faire plus sophistiqué en mettant un troisième nombre qui représentera de combien i doit être augmenté à chaque passage dans la boucle. Par exemple : i = 2, 11, 3 signifie que i, en démarrant de 2 devra aller jusqu'à 11 en étant augmenté de 3 à chaque passage dans la boucle. Par conséquent, nous aurons 4 passages dans la boucle avec i ayant, à chaque passage, respectivement les valeurs 2, 5, 8, 11.

Il est possible aussi de faire en sorte que i soit décrémenté à chaque passage dans la boucle. Par exemple, si l’on écrit : i = 17, 9, -2. Cela signifie que i va aller de 17 à 9 en étant décrémenté de 2 à chaque passage dans la boucle. i va donc prendre à chaque passage dans la boucle respectivement les valeurs 17, 15, 13, 11 et 9 et l’on aura donc 5 passages dans la boucle.

La boucle for..do est très pratique pour manipuler des tables. Nous allons faire un programme qui fait automatiquement pour tous les jours de la semaine ce que le programme du premier chapitre faisait pour un seul jour. Dans le Module:Traduit, nous rajoutons la fonction baratin2 ainsi rédigée :

(19)

local semaine = {"lundi", "mardi", "mercredi", "jeudi", "vendredi",

"samedi", "dimanche"}

local week = {"monday", "tuesday", "wednesday", "thursday", "friday",

"saturday", "sunday"}

function p.baratin2(frame)

local reponse = "<u>Traduction des jours de la semaine</u> <br />"

for index = 1, 7 do

reponse = reponse.."La traduction de

"..semaine[index]..", en anglais, est "..week[index].."<br />" end

return reponse

end

return p

En écrivant : {{#invoke:Traduit|baratin2}}, nous obtenons : Traduction des jours de la semaine

La traduction de lundi, en anglais, est monday La traduction de mardi, en anglais, est tuesday La traduction de mercredi, en anglais, est wednesday La traduction de jeudi, en anglais, est thursday La traduction de vendredi, en anglais, est friday La traduction de samedi, en anglais, est saturday La traduction de dimanche, en anglais, est sunday

Nous remarquons que le wikicode est accepté puisque nous avons réussi à aller à la ligne avec <br /> et nous avons réussi à souligner le titre avec les balises <u></u>. Dans ce programme, nous avons commencé par déclarer la variable reponse en

l'initialisant avec le titre. Nous avons ensuite utiliser une boucle for..do avec une variable d'index allant de 1 à 7. Cette variable d'index servant à accéder aux différentes cases du tableau. La boucle for..do s’avère donc être un excellent moyen pour manipuler la totalité des données se trouvant dans une table.

Deuxième forme de la boucle for[modifier | modifier le wikicode]

Cette deuxième forme de la boucle for appelée aussi forme itérative de la boucle for est plus particulièrement adaptée aux tables. Elle se rédige sous la forme :

for clé, objet in fonction itérative, table, arrêt do

instructions

end

Durant le parcours de la table, clé prend la valeur de la clé considérée et objet prend la valeur correspondante. fonction itérative est une fonction gérant le parcourt de la table, elle gère le parcourt des clés qui nous intéresses dans l’ordre qui nous

intéresse. table est la table considérée et arrêt est la valeur qui stoppe le parcourt de la table, le plus souvent nil.

(20)

Exemple concernant les tables à clé numérique

Dans le Module:Iteratif, nous écrirons la fonction p.description accompagnée de la fonction suivant ainsi :

local souk = {"flute", "pipo", "manche à balaie", "serpière", "jeu de cartes", "coton tige", "tourne vis", "rateau", "stylo", "poupée"}

function suivant(tab,n)

if n == nil then n = 0 end if tab[n+1] == nil then

return nil,nil else return n+1,tab[n+1] end end function p.description() local reponse = " "

for index, objet in suivant,souk,nil do

reponse = reponse.."<br />à la clé numéro "..index.." se trouve l’objet "..objet.."."

end

return reponse

end

La fonction suivant est la fonction itérative qui gère le parcours de la table. Cette fonction retourne deux valeurs : la valeur de la clé suivante et l’objet correspondant. Ici, on se contente d'augmenter la clé d'une unité à chaque boucle. Cette fonction a deux entrées, la table considérée et la valeur de la clé précédente à partir de laquelle elle devra calculer la clé suivante. Au début, la valeur de la clé précédente n'existe pas et la valeur nil est fournie à la place. La fonction suivant voyant nilcomme clé précédente devra fournir, comme clé suivante, la valeur de la première clé à considérer. Lorsque le parcours de la table sera achevé, la variable lue dans la table sera nil et la fonction retournera donc nil comme valeur de la clé suivante indiquant ainsi à la boucle for que le parcours de la table est terminé.

{{#invoke:iteratif|description}} nous retourne :

à la clé numéro 1 se trouve l’objet flute. à la clé numéro 2 se trouve l’objet pipo.

à la clé numéro 3 se trouve l’objet manche à balaie. à la clé numéro 4 se trouve l’objet serpière.

à la clé numéro 5 se trouve l’objet jeu de cartes. à la clé numéro 6 se trouve l’objet coton tige. à la clé numéro 7 se trouve l’objet tourne vis. à la clé numéro 8 se trouve l’objet rateau. à la clé numéro 9 se trouve l’objet stylo. à la clé numéro 10 se trouve l’objet poupée.

Dans la pratique, le plus souvent, le parcours de la table commence à la clé 1 et s'opère ainsi en incrémentant d'une unité la valeur de la clé jusqu'à atteindre la clé de valeur la

(21)

plus élevée comme dans l'exemple précédent. Par conséquent, pour simplifier, le Lua fournit, dans ce cas particulier, une fonction préprogrammée nommée ipairs qui retourne trois valeurs en sortie, à savoir la fonction itérative, la table, et la valeur nil. Grâce à la fonction ipairs, l'exemple précédent se simplifie ainsi :

local souk = {"flute", "pipo", "manche à balaie", "serpière", "jeu de cartes", "coton tige", "tourne vis", "rateau", "stylo", "poupée"}

function p.description()

local reponse = " "

for index, objet in ipairs(souk) do

reponse = reponse.."<br />à la clé numéro "..index.." se trouve l’objet "..objet.."."

end

return reponse

end

Nous étudierons de façon plus approfondie la fonction ipairs dans le chapitre sur les fonctions basiques.

Exemple concernant les tables à clé quelconque

Nous avons vu, dans l'exemple précédent, comment parcourir une table à clé numérique en utilisant la deuxième forme de la boucle for. Supposons que nous voulions faire de même avec une table avec clé sous forme de chaîne de caractères. Le problème qui se pose est de savoir comment écrire la fonction itérative que nous avons

appelée suivant dans l'exemple précédent. Comment passer d'une clé sous forme de chaîne de caractère à la suivante sans en oublier. Nous voyons que le problème n’est pas facile à résoudre. Pour nous faciliter la chose, le Lua fournit une fonction

préprogrammée appelé next qui joue exactement le même rôle que la

fonction suivant de l'exemple précédent, mais pour les clés sous forme de chaîne de caractères.

Prenons un exemple :

local p = {}

local fouillis = {["Nourriture"] = "Fromage", ["Boisson"] =

"Limonade", ["Bestiole"] = "Cafard", ["Couvert"] = "Fourchette", ["Truc"] = "Machin chose"}

function p.farfouille()

local reponse = " "

for index, objet in next,fouillis,nil do

reponse = reponse.."<br />à la clé "..index.." se trouve l’objet "..objet.."."

end

return reponse

(22)

return p

{{#invoke:iteratif|farfouille}} nous retourne :

à la clé Nourriture se trouve l’objet Fromage. à la clé Boisson se trouve l’objet Limonade. à la clé Couvert se trouve l’objet Fourchette. à la clé Truc se trouve l’objet Machin chose. à la clé Bestiole se trouve l’objet Cafard.

Comme pour les tables à clé numérique, le Lua nous facilite un peu les choses en fournissant aussi une fonction préprogrammée appelée pairs qui retourne trois valeurs : une fonction itérative, la table considérée, la valeur nil. Nous pouvons donc simplifier un peu le programme précédent en l'écrivant :

local p = {}

local fouillis = {["Nourriture"] = "Fromage", ["Boisson"] =

"Limonade", ["Bestiole"] = "Cafard", ["Couvert"] = "Fourchette", ["Truc"] = "Machin chose"}

function p.farfouille()

local reponse = " "

for index, objet in pairs(fouillis) do

reponse = reponse.."<br />à la clé "..index.." se trouve l’objet "..objet.."."

end

return reponse

end

return p

Cette fois, nous n'avons pas gagné grand-chose !

Opérateurs logiques

Nous avons vu que certaines structures de contrôle dépendent d'une condition. Nous allons, dans ce paragraphe, étudier plus en détail la formulation de la condition.

Nous avons, jusqu'à maintenant, principalement vu que les variables pouvaient contenir deux types de données que l’on a appelés chaîne de caractères et nombre. Nous allons voir maintenant un nouveau type de donnée que l’on appelle booléen. Une variable de type booléen peut être affectée de deux façons, soit avec la valeur false, soit avec la valeur true. Si elle mémorise la valeur false, cela signifie qu'elle mémorise que quelque chose est faux. Si elle mémorise la valeur true, cela signifie qu'elle mémorise que quelque chose est vrai.

Pour indiquer qu'une variable est de type booléen, on peut l'initialiser avec les valeurs true ou false

(23)

local correct = true local panne = false

true et false sont des mots réservés du langage Lua.

Il est possible aussi de les initialiser avec quelque chose de vrai ou de faux :

local correct = 2 > 1

local panne = 2 < 1

Dans l'exemple ci-dessus, après initialisation, correct contiendra true et panne contiendra false

Les variables en Lua, peuvent devenir de type booléen après affectation, même si elles sont initialisées d'un autre type.

Par exemple, dans le Module:Logique, nous écrirons la fonction essai1 ainsi :

local p = {}

function p.essai1()

local reponse = "Coucou, c’est moi !"

reponse = 2 > 1

return reponse

end

return p

En tapant : {{#invoke:Logique|essai1}}, nous obtenons : true

La variable reponse, qui était de type chaîne de caractère, est devenue de

type booléen après affectation de 2 > 1 et a pris la valeur true car 2 est bien supérieur à 1

N'oublions pas que nous sommes dans le chapitre sur les structures de contrôle. Dans une structure de contrôle : if..then..else, while..do ou repeat..until, nous pouvons nous servir des variables de type booléen comme condition. Si la variable contient le

booléen true, la condition sera considérée comme vraie. Si la variable contient le booléen false, la condition sera considérée comme fausse.

Par exemple, toujours dans le Module:Logique, écrivons une fonction essai2 ainsi :

local p = {}

function p.essai2()

local condition = false

if condition then

return "La condition est vraie" else

(24)

return "La condition est fausse" end

end

return p

En tapant : {{#invoke:Logique|essai2}}, nous obtenons : La condition est fausse Il est possible de se servir d'autres variables ou valeurs comme condition dans les boucles. Il suffit de savoir que toutes les variables ou valeurs différentes de nil sont assimilables à true. Seule une variable contenant nil ou une valeur égale à nil est assimilable à false

Par exemple, toujours dans le Module:Logique, écrivons une fonction essai4 ainsi :

local p = {}

function p.essai4()

local condition = 0

if condition then

return "La condition est vraie"

else

return "La condition est fausse" end

end

return p

En tapant : {{#invoke:Logique|essai4}}, nous obtenons : La condition est vraie

Nous remarquons que 0 a été assimilé à true contrairement à d’autre langage, comme le langage C, où 0 est assimilé à false. Ceci procède d'une certaine logique car, en

langage C, une fonction qui se déroule mal retourne, en principe, 0 pour indiquer que cela s'est mal passé alors qu'en Lua, si cela se passe mal, la fonction

retourne nil. Nil n'existe pas en langage C, son équivalent est 0.

Autre exemple : toujours dans le Module:Logique, écrivons une fonction essai5 ainsi :

local p = {}

function p.essai5()

local condition

if condition then

return "La condition est vraie"

else

(25)

end end

return p

En tapant : {{#invoke:Logique|essai5}}, nous obtenons : La condition est fausse

la variable condition n'a pas été assignée, donc contient nil, et nous voyons que nil est assimilé à false.

De même que les variables de type nombre peuvent être combinées avec les opérateurs +, -, *, /, ^,

de même que les variables de type chaîne de caractères peuvent être combinées avec l'opérateur ..,

les variables de type booléen peuvent être combiné avec les opérateurs and, or, not.

L'opérateur and

Soit a et b deux variables booléennes et soit l'affectation :

c = a and b

alors c sera vraie uniquement si les deux variables a et b sont toutes les deux vraies. Plus précisément, on peut faire un tableau, appelé table de vérité, donnant la valeur de c en fonction des valeurs de a et b, ainsi :

a b c

false false false false true false true false false true true true

L'opérateur or

Soit a et b deux variables booléennes et soit l'affectation :

c = a or b

alors c sera vraie si au moins l'une des deux variables a ou b est vraie. Plus précisément, on peut faire un tableau, appelé table de vérité, donnant la valeur de c en fonction des valeurs de a et b, ainsi :

(26)

false false false false true true

true false true true true true

L'opérateur not

Soit a une variable booléenne et soit l'affectation :

c = not a

alors c sera vraie si a est fausse et sera fausse si a est vraie. Plus précisément, on peut faire un tableau, appelé table de vérité, donnant la valeur de c en fonction de la valeur de a, ainsi :

a c

false true true false

Il est, bien sûr, possible d'opérer directement sur les conditions sans passer par des variables. Toujours dans le Module:Logique, considérons la fonction essai3 qui nous indique si l'argument entré est strictement compris entre 1 et 8 :

local p = {}

function p.essai3(frame)

local n = tonumber(frame.args[1])

if 1 < n and n < 8 then

return "Le nombre est strictement compris entre 1 et 8"

else

return "Le nombre n’est pas strictement compris entre 1 et 8"

end end

return p

Si nous écrivons : {{#invoke:Logique|essai3|6}}, nous obtenons : Le nombre est strictement compris entre 1 et 8

(27)

local p = {}

function p.essai3(frame)

local n = tonumber(frame.args[1])

if 1 < n < 8 then

return "Le nombre est strictement compris entre 1 et 8"

else

return "Le nombre n’est pas strictement compris entre 1 et 8"

end end

return p

nous obtenons une erreur de script !

Opérateurs externes

Nous dirons qu'un opérateur est externe si le résultat de l'opération est d'un type différent de celui des objets sur lesquels s'effectue l'opération

Par exemple, nous avons écrit plus haut :

local correct = 2 > 1

local panne = 2 < 1

Ce qui nous montre que > et < sont des opérateurs externes car il effectue une opération entre les nombres 1 et 2 et le résultat de cette opération est un booléen qui sera affecté aux variables correct ou panne.

> est l'opérateur "strictement supérieur". Nous avons aussi l'opérateur "supérieur ou

égal" qui se noterait >=.

< est l'opérateur "strictement inférieur". Nous avons aussi l'opérateur "inférieur ou égal"

qui se noterait <=.

Nous avons aussi déjà utilisé l'opérateur == qui permet de comparer deux variables et qui retourne "true" si les variables sont égales ou "false" si les variables sont différentes. Nous avons aussi l'opérateur ~= qui compare aussi deux variables mais retourne "false" si les variables sont égales et "true" si les variables sont différentes.

Références

Documents relatifs

L’archive ouverte pluridisciplinaire HAL, est destinée au dépôt et à la diffusion de documents scientifiques de niveau recherche, publiés ou non, émanant des

Dans l'univers sonore du romancier, le terrain d'union entre les personnes n'existe que pour des individus curieux d'ouïr ; mais cette curiosité est liée aux tour- ments que les

Une collaboration s'établit alors entre le CRESSON et le LISI avec les objectifs d’explorer l’outil SIG pour organiser un système de représentation cartographique de

Face aux répercussions de l’invasion numérique des médias traditionnels, qui a parallèlement été à l’origine de la production de divers réseaux sociaux, nous devons faire

Test &amp; Flip (TF) nets, a mild extension of Elementary Net Systems (with or without inhibitor arcs) [3,9] and Flip-Flop nets [10], are proposed to express surgical process

لا ملاعلإا&#34; كرتشملا امهباتك يف ديدج ا ةسايسلاو :يهو عاونأ ةثلاث قفو ديدجلا ملاعلإا عضي &#34; ةيكيرملأ * ةميدق ايجولونكتب ديدجلا ملاعلإا :

- امبو نأ لاصتلااو ملبعلإا مومع اءزج لثمت نم فمتخم مومعلا يتلا دمتعت يف ايثوحب ىمع تايرظنلا ،ةيعامتجلااو ةيلاصتلاا ارظنو بعشتل ، دقعت كباشتو

paysage urbain : « J’interprète Barcelone dans les dernières décennies des années 2000 par la destruction caractérisée d’une bonne part de sa mémoire – cette même ville, la