Initiation au Lua avec Scribunto/Version imprimable
Une version à jour et éditable de ce livre est disponible sur la Wikiversité,
une bibliothèque de livres pédagogiques, à l'URL :
http://fr.wikiversity.org/wiki/Initiation_au_Lua_avec_Scribunto
Premières notions
Dans ce premier chapitre, nous allons progressivement introduire le minimum nécessaire pour pouvoir commencer à écrire des petits programmes très simples en Lua.
Premier module
modifierL'espace module
modifierPour écrire un programme en Lua, nous avons, tout d’abord, besoin d'un endroit pour l'écrire. De même que les modèles s'écrivent dans des pages nommées : Modèle:«Nom de la page», les programmes Lua s'écriront dans des pages nommées : Module:«Nom de la page». On dira que l’on écrit dans l'espace module.
Une fois le programme écrit dans l'espace module, nous devons être capable de l’utiliser à partir d'une autre page (de même qu'un modèle peut être inclus dans une page en écrivant {{Nom du modèle}}
).
Exemple de module
modifierNous allons prendre un exemple très simple pour bien comprendre. Supposons que l’on veuille écrire un programme qui, lorsqu'on l'appelle, nous renvoie le message : « Coucou, c’est moi ! ». Nous commencerons par créer une page du style : Module:Exemple simple et dans cette page, nous écrirons :
local p = {}
function p.Salutation()
return "Coucou, c’est moi !"
end
return p
Nous allons vous expliquer ce que signifie chacune des lignes du module.
La table p
modifierTout d’abord, nous avons une première ligne : local p = {}
. Le mot local
est réservé par le langage Lua (de tels mots-clés apparaissent en couleur). Cela signifie que lorsqu’il sera employé, il aura un sens qui a été défini par les concepteurs du langage Lua. Ce mot nous indique que ce qui est écrit juste après n'est valable que dans la page ou à l'intérieur d'un bloc d'instructions (nous reviendrons sur cette notion quand nous préciserons ce que l’on entend par « bloc d'instructions »). Après le mot local
, nous voyons : p = {}
. p est une table (dans d'autres langages, on dit plutôt tableau). Dans un langage de programmation, une table (ou tableau) peut être vue comme une armoire avec des tiroirs, dans lesquels on peut ranger toutes sortes d'objets (nous verrons lesquels, plus tard). Comment savons-nous que p est une table ? C'est à cause de = {}
. Les accolades ouvrantes et fermantes symbolisent une table en Lua (attention, ce n’est pas vrai dans tous les langages !). La ligne : local p = {}
signifie donc : « p est une table qui ne peut être utilisée que dans ce module ».
Retenez bien, si vous ne le savez pas, que ce que vous apprenez ici est propre au langage Lua. Les créateurs de langages, assez mystérieusement, prennent plaisir à nous embrouiller en définissant les choses différemment. En langage C par exemple, un tableau ne se définit pas de la même manière. |
La fonction p.Salutation
modifierNous avons ensuite les lignes :
function p.Salutation()
return "Coucou, c’est moi !"
end
function
est un autre mot-clé qui sert à définir une fonction. Ici, on définit la fonction p.Salutation qui sera rangée dans la table p (l'un des tiroirs de l'armoire p, pour reprendre l'analogie utilisée plus haut), c’est pour cela que l’on écrit function p.Saluation
. Si l’on avait écrit seulement function Salutation()
, on aurait bien défini une fonction Salutation mais elle ne se trouverait pas dans la table p. Il est nécessaire de mettre la fonction p.Salutation dans la table p pour que cette fonction puisse être retournée en même temps que le contenu de la table p grâce à l'instruction return p
se trouvant en fin de programme (nous reviendrons là-dessus plus tard).
En dessous de la définition de la fonction, nous avons la ligne : return "Coucou, c’est moi !"
qui constitue le bloc d'instructions décrivant le programme de la fonction. Nous disons bloc d'instructions car il y aurait pu y avoir plusieurs instructions si la fonction avait été plus complexe. Ce bloc d'instructions se terminant par le mot-clé end
indiquant que la programmation de la fonction est terminée.
Si l’on analyse plus en détail l'instruction : return "Coucou, c’est moi !"
, nous voyons qu'elle débute par le mot-clé return
. return
indique ce qui doit être retourné par la fonction. Le rôle d'une fonction est, la plupart du temps, de retourner quelque chose. En mathématiques la fonction f(x) = 3x + 2 retourne le résultat du calcul de l’expression 3x + 2 en remplaçant x par un nombre particulier. En Lua, on programmerait cette fonction ainsi :
function f(x)
return 3 * x + 2
end
Nous remarquons les parenthèses ouvrante (
et fermante )
, présentes aussi dans notre programme dans la ligne : function p.Salutation()
. Mais il n'y a rien à l'intérieur car la fonction p.Salutation se contente de retourner une phrase sans qu'on lui transmette aucune information (il n'en sera pas toujours ainsi).
Après le mot-clé : return
, nous avons : "Coucou, c’est moi !"
, qui est la phrase que doit retourner la fonction p.Salutation. Nous remarquons la présence des guillemets qui indiquent que c’est une chaîne de caractères. Ces guillemets ont deux fonctions. D'abord, ils indiquent où commence la chaîne de caractères et où celle-ci finit. Ensuite, ils indiquent que l’on a bien affaire à une chaîne de caractères et pas à une variable (nous reviendrons là-dessus plus bas lorsque nous étudierons plus en détail ce qu'est une variable).
Utiliser un module
modifierDans notre programme, après l'écriture de la fonction, nous voyons apparaître la ligne : return p
. Cette instruction, écrite en dehors de la fonction, indique que l’on retourne le contenu de la table p pour le rendre disponible en dehors du module. Nous précisons que nous retournons le contenu de la table p et pas la table p elle-même, celle-ci n'étant disponible qu’à l'intérieur du module à cause de l'instruction local p = {}
.
Comment allons-nous pouvoir récupérer le contenu de la table p, à savoir la fonction p.Salutation en dehors du module ? Pour cela, il nous suffit d'écrire dans une autre page qui n’est pas dans l'espace module, la commande : {{#invoke:Exemple simple|Salutation}}
.
En effet, en tapant : {{#invoke:Exemple simple|Salutation}}
, on obtient bien : « Coucou, c’est moi ! ».
Nous retiendrons la syntaxe de cette commande sous la forme provisoire : {{#invoke:nom du module|nom de la fonction}}
. Nous allons voir dans les paragraphes suivants que l’on peut y rajouter des paramètres.
Paramétrer une fonction
modifierExemple avec un seul paramètre
modifierPour illustrer le passage d'un paramètre à une fonction écrite dans un module et ceci à partir d'une page extérieure au module, nous allons écrire une nouvelle fonction dans un nouveau module que l’on appellera, par exemple, Module:Autre exemple.
Nous nous proposons cette fois d'écrire un programme qui va traduire les jours de la semaine en anglais.
Le programme à l'intérieur du Module:Autre exemple sera rédigé ainsi :
local p = {}
function p.traduit(frame)
if frame.args[1] == "Lundi" then return "Monday" end
if frame.args[1] == "Mardi" then return "Tuesday" end
if frame.args[1] == "Mercredi" then return "Wednesday" end
if frame.args[1] == "Jeudi" then return "Thursday" end
if frame.args[1] == "Vendredi" then return "Friday" end
if frame.args[1] == "Samedi" then return "Saturday" end
if frame.args[1] == "Dimanche" then return "Sunday" end
end
return p
Nous ne commenterons, bien sûr, que ce qui est nouveau par rapport au paragraphe précédent.
Dans la définition de la nouvelle fonction : function p.traduit(frame)
, nous remarquons que, cette fois, nous avons entre parenthèses, le mot frame
.
Par convention, on peut dire que frame est une table qui contient les paramètres que l’on a passés au module depuis l'extérieur grâce à la commande vue au paragraphe précédent qui, dans ce paragraphe, aura la syntaxe plus complète suivante : {{#invoke:''nom du module''|''nom de la fonction''|args[1]|args[2]|args[3]|etc.}}
. args[1], args[2], args[3], args[4] étant des informations que l’on souhaite transmettre à la fonction que l’on a choisie dans le module. Ces informations, appelées paramètres, se retrouveront dans le module dans la table frame
. Pour y accéder, il suffira donc d'écrire dans le programme respectivement : frame.args[1], frame.args[2], frame.args[3], etc. Par exemple, supposons que nous voulions utiliser notre programme pour traduire « jeudi » en anglais. Nous écrirons simplement {{#invoke:Autre exemple|traduit|Jeudi}}
et nous obtenons : « Thursday ».
Dans le corps de la fonction, nous avons cette fois un bloc d'instructions comprenant 7 instructions similaires et nous comprenons aisément que chacune de ces instructions correspond à un jour de la semaine. Prenons la première : if frame.args[1] == "Lundi" then return "Monday" end
. Nous avons, dans cette instruction deux nouveaux mots-clés : if
et then
. if
se traduit par « si » en français et then
se traduit par « alors ». Le syntaxe générale de cette instruction est if condition then instructions end
(ou, en français : « si condition alors instructions fin »). Autrement dit, de façon plus explicite : « si une certaine condition est réalisée alors le bloc d'instructions sera exécuté ».
Dans notre instruction : if frame.args[1] == "Lundi" then return "Monday" end
, nous voyons que la condition est frame.args[1] == "Lundi"
. On se demande si le paramètre transmis à la fonction est la chaîne de caractère « Lundi ». En Lua, ==
signifie « égal » (si l’on met seulement =
, le sens ne sera pas le même ; nous y reviendrons). Si la condition testée est remplie alors on exécutera le bloc d'instructions qui, dans notre exemple, se limite à return "Monday"
. Nous voyons que la fonction p.traduit va retourner la chaîne de caractères « Monday » si le paramètre transmis est « Lundi » et c’est ce que l’on voulait. Le raisonnement est le même pour les six autres lignes qui suivent.
Exemple avec plusieurs paramètres
modifierNous allons maintenant essayer de perfectionner le programme vu au paragraphe précédent en lui faisant traduire les jours de la semaine dans une langue que l’on aura choisie. Nous nous limiterons à l'anglais et à l'espagnol. Le lecteur comprendra aisément comment introduire d'autres langues.
Le programme est le suivant :
local p = {}
function p.traduit(frame)
if frame.args[2] == "Anglais" then
if frame.args[1] == "Lundi" then return "Monday" end
if frame.args[1] == "Mardi" then return "Tuesday" end
if frame.args[1] == "Mercredi" then return "Wednesday" end
if frame.args[1] == "Jeudi" then return "Thursday" end
if frame.args[1] == "Vendredi" then return "Friday" end
if frame.args[1] == "Samedi" then return "Saturday" end
if frame.args[1] == "Dimanche" then return "Sunday" end
end
if frame.args[2] == "Espagnol" then
if frame.args[1] == "Lundi" then return "Lunes" end
if frame.args[1] == "Mardi" then return "Martes" end
if frame.args[1] == "Mercredi" then return "Miércoles" end
if frame.args[1] == "Jeudi" then return "Jueves" end
if frame.args[1] == "Vendredi" then return "Viernes" end
if frame.args[1] == "Samedi" then return "Sábato" end
if frame.args[1] == "Dimanche" then return "Domingo" end
end
end
return p
Nous avons mis ce programme dans le Module:Traduction multilingue. Pour l’utiliser, nous devons préciser deux paramètres. Par exemple, pour obtenir la traduction de dimanche en espagnol, nous devons écrire dans la page appelante : {{#invoke:Traduction multilingue|traduit|Dimanche|Espagnol}}
, ce qui nous donne : « Domingo ». À l'intérieur du programme, les deux paramètres sont accessibles respectivement par frame.args[1]
et frame.args[2]
.
Le programme, en lui-même, ne présente pas de nouvelles instructions. Nous remarquerons toutefois la possibilité d’emboîter les structures if condition then instructions end
.
Par exemple, la première structure concernée s'écrit sous la forme :
if frame.args[2] == "Anglais" then
-- Bloc d'instructions --
end
Le bloc d'instructions comprenant les 7 traductions en anglais sous forme de 7 structures if condition then instruction end
.
Concaténer du texte
modifierLa concaténation est une opération qui consiste à mettre bout à bout plusieurs chaînes de caractères. L'opérateur de concaténation est ..
(deux points qui se suivent). Pour illustrer cela, nous allons écrire un Module:Faire part qui, à partir d'un ou deux noms entrés en paramètres, forme une phrase annonçant, soit une naissance, soit un mariage, soit un décès.
Le contenu du module est le suivant :
local p = {}
function p.naissance(frame)
return "Nous avons la joie de vous annoncer la naissance de " .. frame.args[1] .. "."
end
function p.mariage(frame)
return "Nous sommes heureux de vous annoncer le mariage de " .. frame.args[1] .. " et " .. frame.args[2] .. "."
end
function p.deces(frame)
return "Nous sommes au regret de vous annoncer le décès de " .. frame.args[1] .. "."
end
return p
Nous remarquons que ce module contient, cette fois, trois fonctions. Donnons des exemples d'utilisation de ce module.
Si dans la page appelante, nous écrivons {{#invoke:Faire part|mariage|Louis|Christine}}
, nous obtenons :
- « Nous sommes heureux de vous annoncer le mariage de Louis et Christine. »
Si dans la page appelante, nous écrivons {{#invoke:Faire part|naissance|Noémie}}
, nous obtenons :
- « Nous avons la joie de vous annoncer la naissance de Noémie. »
Si dans la page appelante, nous écrivons {{#invoke:Faire part|deces|monsieur Bertelot}}
, nous obtenons :
- « Nous sommes au regret de vous annoncer le décès de monsieur Bertelot. »
Par exemple, pour le faire part de mariage, l'instruction return
retourne une chaîne de caractères obtenue en concaténant avec ..
:
"Nous sommes heureux de vous annoncer le mariage de "
(on remarque la présence d'une espace en fin de chaîne pour éviter d’avoir le premier nom collé au mot « de ») ;frame.args[1]
(contenant dans notre exemple la chaîne de caractères formant le prénom Louis) ;" et "
(on remarque aussi les espaces en début et en fin de chaîne pour éviter d’avoir LouisetChristine) ;frame.args[2]
(contenant dans notre exemple la chaîne de caractères formant le prénom Christine) ;"."
(chaîne de caractères se limitant au point final de la phrase).
Dans ce module, nous remarquons que nous avons écrit la fonction p.deces sans accents. En effet, le Lua n'accepte pas les accents dans les noms de fonctions et plus généralement dans les noms de variables.
Variables, types et affectation
modifierNous allons maintenant aborder une notion importante qui est la notion de variable. D'une façon imagée, on pourrait dire qu'une variable est une boîte dans laquelle on va pouvoir mettre un objet particulier qui sera, soit une chaîne de caractères, soit un nombre, soit une table, soit une fonction, soit un booléen, etc. En Lua, une même variable peut, dans un même programme, contenir, par exemple, une chaîne de caractères dans une partie du programme et dans une autre partie, elle contiendra un nombre. Cette faculté de recevoir des objets de nature différentes se traduit en disant que les variables, en Lua, sont dynamiques. Ce n’est pas le cas, dans d'autres langages comme le C ou le Pascal où les variables sont dites statiques (chacune étant spécialisée pour recevoir un seul type d'objet).
La plupart du temps, une variable se déclare grâce à l'instruction :
local tirelire
Le mot local
signifie qu'elle n'est opérationnelle que là où on l'a déclarée. Si on la déclare en début de module, elle sera valable dans tout le module (y compris dans les fonctions du module). Si on la déclare au début d'une fonction, elle sera valable uniquement dans la fonction (y compris dans les structures de contrôle comme if..then
). Si on la déclare au début d'une structure de contrôle, elle sera valable uniquement dans la structure de contrôle.
Il est possible, à la déclaration, de l'initialiser, c'est-à-dire d'y mettre quelque chose dedans. Dans ce cas, la variable adoptera le type de ce que l’on met dedans.
Si on ne l'initialise pas, la variable sera, par défaut, d'un type particulier que l’on appelle nil et contiendra nil
, ce qui signifie « rien ». Elle restera de ce type jusqu'à ce qu'on y mette quelque chose.
Par exemple, pour l'instruction :
local tirelire = "Billet de dix euros"
la variable tirelire sera du type chaîne de caractères et sera censée être du type chaîne de caractères jusqu'à ce que l’on y mette quelque chose qui ne soit pas une chaîne de caractère.
Le signe =
présent dans cette instruction ne veut pas dire « égal » mais « affectation ». À la variable tirelire, on affecte la chaîne de caractères : "Billet de dix euros"
.
Si l’on écrit :
local tirelire = 10
la variable tirelire sera du type nombre et sera censée être du type nombre jusqu'à ce que l’on y mette quelque chose qui ne soit pas un nombre.
Même si une variable peut contenir à des moments différents, des objets de type différent, il faut malgré tout que l’on sache ce qu'elle est susceptible de contenir dans toutes les parties du programme. Dans certains cas, le programme peut ne pas fonctionner si la variable n'a pas le bon type au bon moment.
Prenons un exemple : écrivons un programme qui nous signale si un nombre n'a pas une valeur trop élevée. Dans le Module:Balance, écrivons le programme suivant :
local p = {}
function p.alerte1(frame)
local poids = frame.args[1]
local reponse = "Votre poids est acceptable"
if poids > 54 then
reponse = "Attention, vous commencez à grossir !"
end
return reponse
end
return p
Si l’on écrit, dans une autre page : {{#invoke:Balance|alerte1|56}}
, on obtient : « Erreur Lua dans Module:Balance à la ligne 6 : attempt to compare number with string. »
Pour comprendre pourquoi le programme ne marche pas, nous allons revenir sur un exemple précédent. Reprenons, par exemple, le programme qui traduisait les jours de la semaine en anglais. Pour traduire jeudi en anglais, nous avions écrit : {{#invoke:Autre exemple|traduit|Jeudi}}
. Jeudi est une chaîne de caractères et pourtant, nous n'avons pas écrit : {{#invoke:Autre exemple|traduit|"Jeudi"}}
. La commande invoke
a interprété jeudi comme étant une chaîne de caractères même si nous n'avons pas mis les guillemets. Pour simplifier l'écriture, la commande invoke
interprète systématiquement ses arguments comme étant des chaînes de caractères. Par conséquent, lorsqu'on écrit : {{#invoke:Balance|alerte1|56}}
, le programme reçoit comme argument la chaîne de caractères "56"
et non pas le nombre 56
. Par conséquent, l'instruction de notre programme :
local poids = frame.args[1]
affecte à la variable poids la chaîne de caractères "56"
, et l'instruction :
if poids > 54 then
reponse = "Attention, vous commencez à grossir !"
end
compare donc une chaîne de caractères au nombre 54, ce qui n'a pas de sens.
Pour remédier à cet inconvénient, il faut, avant de faire la comparaison avec 54, transformer le contenu de la variable poids en nombre. Comment faire ? Heureusement, dans le Lua, nous disposons d'un certain nombre de petites fonctions préprogrammées que nous étudierons en détail dans les chapitres ultérieurs. Pour les besoins de la circonstance, nous allons utiliser l'une d'elles ici. Cette fonction est la fonction tonumber qui convertit une chaîne de caractères en nombre dans la mesure où cela est possible. Par exemple, elle convertira la chaîne de caractères "12"
en nombre 12.
Nous pouvons mettre en œuvre cette fonction en écrivant dans notre programme :
local poids = tonumber(frame.args[1])
Et nous écrirons donc, dans le Module:Balance, une nouvelle fonction p.alerte2, qui est la version corrigée de la fonction p.alerte1, ainsi :
local p = {}
function p.alerte2(frame)
local poids = tonumber(frame.args[1])
local reponse = "Votre poids est acceptable"
if poids > 54 then
reponse = "Attention, vous commencez à grossir !"
end
return reponse
end
return p
Maintenant, si dans une autre page, on écrit : {{#invoke:Balance|alerte2|56}}
, on obtient : « Attention, vous commencez à grossir ! »
On constate que cette fois, ça marche !! (du moins le programme, pas le régime !)
Nous allons maintenant étudier une particularité du Lua. Dans une affectation, le Lua a la capacité de caractériser le type des variables automatiquement en fonction de ce qui est affecté. Si, par exemple, dans ce qui est affecté, il y a des signes opératoires, la variable sera de type nombre. S'il y a des concaténations, la variable sera du type chaîne de caractères.
Premier exemple :
compte = a + b
La variable compte sera automatiquement considérée comme étant du type nombre à cause de la présence de l'opérateur +
, sans même s'occuper de ce que contiennent les variables a et b. Et ceci, même si a et b contiennent des chaînes de caractères. Si a contient la chaîne de caractères "1"
et si b contient la chaîne de caractères "2"
, alors la variable compte contiendra, malgré tout, le nombre 3.
À noter toutefois que si a ou b ne contient pas quelque chose qui puisse être converti en nombre, alors cela génère une erreur et le programme s'arrête. Nous étudierons la gestion des erreurs dans un chapitre ultérieur.
Deuxième exemple :
panneau = indication..route
La variable panneau sera automatiquement considérée comme étant du type chaîne de caractères à cause de la présence de l'opérateur de concaténation ..
, sans même s'occuper de ce que contiennent les variables indication et route. Et ceci, même si indication et route contiennent des nombres. Si indication contient le nombre 1 et si route contient le nombre 2, alors la variable panneau contiendra, malgré tout, la chaîne de caractères "12"
.
Ceci étant dit, nous pouvons revenir à notre programme qui ne marchait pas. Nous avons dit qu’il faudrait que la variable poids contienne un nombre et pas une chaîne de caractères. Pour la transformer, compte tenu de ce que nous venons de dire, certains petits malins auraient pu écrire le programme ainsi :
local p = {}
function p.alerte1(frame)
local poids = frame.args[1] + 0
local reponse = "Votre poids est acceptable"
if poids > 54 then
reponse = "Attention, vous commencez à grossir !"
end
return reponse
end
return p
Ça marche ! Mais :
local poids = frame.args[1] + 0
ne fait pas professionnel et ressemble à du bricolage. Nous abandonnerons donc cette idée au profil de :
local poids = tonumber(frame.args[1])
La structure if..then..else
modifier
Nous venons d'étudier la structure if condition then instructions end
. Il est possible de compléter cette structure en y rajoutant un petit élément qui est else
et qui signifie « sinon » en français. La structure if condition then instruction1 else instruction2 end signifie en français : « si une condition est remplie exécuter instruction1 sinon exécuter instruction2 ». À titre d'exemple, reprenons notre Module:Balance que nous avons commencé à remplir.
Dans ce module, grâce à l'ajout de else
, nous pouvons écrire une fonction p.alerte3, qui est une autre façon d'écrire la fonction p.alerte2, ainsi :
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 ! »
Traiter plusieurs variables en une seule instruction
modifierIl nous reste à voir une particularité intéressante du Lua qu'on ne trouve pas dans d'autres langages : c’est l'affectation simultanée de plusieurs variables. En effet, plutôt que d'écrire :
a = 2
b = 7
c'est-à-dire mettre la valeur 2 dans a, puis la valeur 7 dans b, on peut écrire :
a, b = 2, 7
et tout se passera comme si l’on avait simultanément mis 2 dans a et 7 dans b. Vous allez me dire : bof ! quel intérêt ?
Il y a plusieurs intérêts à cela. Nous verrons certains de ces intérêts dans les chapitres suivants quand nous étudierons les fonctions qui retournent plusieurs valeurs.
Pour le moment, nous pouvons donner un exemple simple : supposons que nous voulions échanger le contenu de deux variables. En Lua, nous écrirons simplement :
a, b = b, a
Dans un autre langage qui n'a pas l'affectation simultanée, on aurait été tenté d'écrire :
a = b
b = a
Mais ça ne marche pas car le contenu de a est remplacé par le contenu de b dans la première affectation. Le contenu initial de a est donc perdu et ne pourra donc pas aller dans b à la deuxième affectation.
On peut aussi déclarer simultanément deux variables en les initialisant :
local a, b = 2, 7
ou déclarer simultanément deux variables sans les initialiser :
local a, b
Mise au point d'un module
Dans le chapitre précédent, nous avons vu suffisamment de notions pour commencer à faire des petits modules faciles à utiliser. Bien souvent les modules ne seront pas nécessairement petits et faciles à utiliser. Leurs mises au point risque d’être délicates à faire. Par conséquent, avant d'aller plus loin dans l'étude du Lua avec Scribunto, nous allons consacrer ce chapitre à l'étude des moyens dont nous disposons pour faciliter la mise au point des modules.
Augmenter la lisibilité du programme se trouvant dans le module
modifierUne première façon de rendre un programme plus facile à mettre au point est de l'écrire de façon à ce qu’il soit facile à relire par nous-même ou par quelqu’un d'autre. Par nous-même, car même si l’on a l'impression, sur le moment, de bien savoir ce qu’il contient, il se peut que l’on soit amené à y revenir après plusieurs mois et là, on risque d’avoir du mal à retrouver comment il fonctionne. Par les autres, car les modules écrits sur un des projets Wikimédia peuvent être améliorés par d'autres utilisateurs.
L'amélioration de la lisibilité d'un programme se base sur trois techniques:
L'indentation
modifierC'est le fait de décaler vers la droite un bloc d'instructions pour le rendre plus lisible. On décalera vers la droite les instructions se trouvant à l'intérieur d'une fonction, d'une structure if condition then instruction end
, et les autres structures de contrôle que nous verrons plus tard.
Par exemple, dans le Module:Traduction multilingue, si nous avions écrit :
local p = {}
function p.traduit(frame)
if frame.args[2] == "Anglais" then
if frame.args[1] == "Lundi" then return "Monday" end
if frame.args[1] == "Mardi" then return "Tuesday" end
if frame.args[1] == "Mercredi" then return "Wednesday" end
if frame.args[1] == "Jeudi" then return "Thursday" end
if frame.args[1] == "Vendredi" then return "Friday" end
if frame.args[1] == "Samedi" then return "Saturday" end
if frame.args[1] == "Dimanche" then return "Sunday" end
end
if frame.args[2] == "Espagnol" then
if frame.args[1] == "Lundi" then return "Lunes" end
if frame.args[1] == "Mardi" then return "Martes" end
if frame.args[1] == "Mercredi" then return "Miércoles" end
if frame.args[1] == "Jeudi" then return "Jueves" end
if frame.args[1] == "Vendredi" then return "Viernes" end
if frame.args[1] == "Samedi" then return "Sàbato" end
if frame.args[1] == "Dimanche" then return "Domingo" end
end
end
return p
Le programme aurait, tout de même, bien fonctionné mais aurait été moins lisible.
Les noms de variable explicites
modifierLe Lua, ainsi que la plupart des langages de programmation, permettent d'écrire les variables en utilisant plusieurs caractères. On donnera donc aux variables un nom qui exprimera ce qu'elles contiennent. Par exemple, une variable destinée à mémoriser un salaire s'appellera salaire.
Les commentaires dans le programme
modifierNous n'en n'avons pas parlé pour raison pédagogique, au chapitre précédent, car les programmes étaient petits et des commentaires les auraient alourdis inutilement. Mais il est possible et même fortement conseillé, quand les programmes deviennent plus longs, de rajouter des commentaires à l'intérieur même des programmes pour expliquer ce que chaque partie du programme fait.
Un petit commentaire tenant sur une ligne se fera en commençant par mettre un double tirets --
. On peut même mettre un commentaire après une instruction.
Par exemple, dans le Module:Faire part, on aurait pu rajouter des commentaires sur ce que réalisent les fonctions :
local p = {}
function p.naissance(frame) -- Faire part de naissance
return "Nous avons la joie de vous annoncer la naissance de " .. frame.args[1] .. "."
end
function p.mariage(frame) -- Faire part de mariage
return "Nous sommes heureux de vous annoncer le mariage de " .. frame.args[1] .. " et " .. frame.args[2] .. "."
end
function p.deces(frame) -- Faire part de décès
return "Nous sommes au regret de vous annoncer le décès de " .. frame.args[1] .. "."
end
return p
On peut aussi imaginer avoir besoin de plusieurs lignes pour faire un commentaire. Pour cela, on commencera le commentaire par --[[
et on le terminera par ]]
.
Par exemple pour Module:Exemple simple on aurait pu écrire :
local p = {}
function p.Salutation() -- [[mon commentaire ………………………………………………
de plusieurs lignes]]
return "Coucou, c’est moi !"
end
return p
L'éditeur Scribunto
modifierPour que les numéros de ligne de l'éditeur apparaissent, il faut cocher l'option « Activer la barre d'outils d'édition » dans les préférences de modification |
Étudions de plus près l'éditeur dont nous disposons dans l’extension Scribunto. Nous remarquons, tout d’abord, que chaque ligne est numérotée. Lorsque nous écrivons une ligne, celle-ci se détache sur un fond légèrement grisé. Nous le voyons à la figure 2, ligne 16 où se trouve le curseur. L'éditeur dispose d'une première correction pour détecter les erreurs grossières dans la syntaxe des instructions. Si nous n'écrivons pas correctement une instruction, le numéro en début de ligne se retrouve précédé d'une croix dans un carré rouge. Nous le voyons, par exemple, dans la figure 1, ligne 16. Si nous regardons, de plus près la ligne 16, nous verrons que nous avons oublié de mettre le mot-clé then
qui doit obligatoirement se trouver dans une structure if condition then instructions end
. Mieux que cela, si un carré rouge avec croix apparaît devant un numéro de ligne, nous pouvons avoir une indication sur le type d'erreur en promenant le curseur dessus (nous voulons dire par là, que nous pointons le carré rouge avec le curseur sans toutefois cliquer dessus). Dans notre exemple, figure 1, nous avons le message « [16:33] 'then' expected near 'return' », ce qui signifie que ligne 16, position 33, then
est attendu avant return
.
Nous remarquons aussi, en début de certaines lignes, un symbole ▼ juste après le numéro de ligne. Ce caractère permet de masquer un bloc d'instructions. Par exemple, si nous cliquons sur le ▼ de la ligne 3 ou est déclarée la fonction p.traduit, nous voyons disparaître toutes les instructions se trouvant entre cette déclaration et le end
indiquant la fin de l'écriture du contenu de la fonction. Si nous cliquons sur le ▼ de la ligne 4, nous verrons disparaître toutes les instructions du bloc if
. L'utilité de cette fonctionnalité est double. On peut ainsi masquer certaines parties du programme sur lesquelles on n’est pas en train de travailler. On peut aussi, dans un programme, où il y a beaucoup de structures emboîtées et par conséquent beaucoup de end
, s'assurer que l’on ne s'est pas « emmêlé les pinceaux » avec les end
.
Une autre particularité intéressante de l'éditeur est que lorsque l’on clique juste après une parenthèse, un crochet ou une accolade ouvrante ou fermante, nous voyons un léger encadrement sur la parenthèse, le crochet ou l'accolade fermante ou ouvrante correspondante. Cela peut être utile dans les expressions ayant beaucoup de parenthèses, crochets et accolades pour éviter les erreurs.
Intéressons-nous maintenant à ce qui apparaît sous le cadre de visualisation. Nous n'allons pas nous intéresser à ce qui est juste en dessous du cadre de visualisation, car il n'y a là rien de bien nouveau. Nous allons nous intéresser à ce qui se trouve plus bas dans le cadre noté « Aperçu de la page avec ce modèle » (voir figure 2). En effet, nous allons pouvoir, avec cet aperçu, voir ce que va donner le module avant même de devoir l'enregistrer. Il est possible, grâce à ce cadre, de faire l'écriture et la mise au point complète du module sans faire une seule édition.
Pour cela, enregistrez tout d’abord dans une page — par exemple Bac à sable — la commande {{#invoke:''nom du module''|''nom de la fonction''|"arguments"}}
concernant votre module. Dans le cadre « Aperçu de la page avec ce modèle » (voir ci-contre), écrivez le titre de la page où le module est invoqué (ici la page « Bac à sable »). Enfin, cliquez sur « Afficher l'aperçu ». L'aperçu de la page où vous avez invoqué votre module (ici l'aperçu de Bac à sable) s'affiche en haut de la page. Si ce n’est pas correct, vous pouvez corriger le module et cliquer à nouveau sur « Afficher l'aperçu » autant de fois que vous voulez, jusqu’à ce que le module soit au point. Une fois le module au point, vous pouvez cliquer sur le bouton « Enregistrer » et le module sera édité.
Le traitement des erreurs de script
modifierAprès avoir corrigé toutes les erreurs indiquées par l'éditeur, nous ne sommes peut-être pas au bout de nos peines. En essayant le programme, nous voyons apparaître le charmant message : « Erreur de script : vous devez spécifier une fonction à appeler. »
Cela signifie que nous avons malgré tout fait une erreur que l'éditeur n'a pas décelée mais qui rend l'exécution du programme impossible.
Avec l'expérience, nous pouvons éviter les principales erreurs de script. En attendant d'acquérir cette expérience, nous nous contenterons d'énumérer les principales situations qui provoquent une erreur de script.
Voici une liste à compléter éventuellement :
- Utilisation d'une variable en croyant qu'elle contient un certain type de données, alors qu'elle en contient un autre. Exemple : comparaison d'une variable contenant une chaîne de caractères avec un nombre.
- Utilisation d'une instruction en dehors du contexte où elle devrait être normalement utilisée. Par exemple, emploi de
frame.args[1]
en dehors de la fonction qui devrait normalement recueillir l'argument. - La fonction appelée n'existe pas. Vous avez, peut-être, fait une faute d'orthographe en écrivant son nom ou simplement oublié
p.
en début de nom. - Opération avec une variable, qui est bien du bon type, mais que l’on n'a pas initialisée et qui est donc vide au moment où on l'utilise.
- Peut éventuellement être produit par l'oubli de l'instruction
return
dans une fonction (selon comment est utilisée la fonction).
Lorsqu'une erreur de script se produit, vous pouvez avoir une première indication sur la provenance de cette erreur en cliquant sur le message : « Erreur de script : vous devez spécifier une fonction à appeler. » (bien qu’il soit rouge et non bleu). L'indication vous permettra, peut-être, de corriger rapidement l'erreur.
Si, malgré tout, l'erreur de script continue à apparaître et que vous ne voyez pas d'où elle provient, vous pouvez utiliser l'astuce suivante :
Vous mettez --
progressivement au début des lignes, en commençant par celles qui paraissent les plus douteuses, jusqu'à ce que l'erreur de script disparaisse. Ces lignes commençant par --
seront alors interprétées comme étant des commentaires et ne pourront plus provoquer d'erreur de script. Vous pourrez ainsi repérer la ligne qui provoque l'erreur de script.
La recherche d'une erreur dans le programme
modifierVous avez écrit un module. L'éditeur n'a pas détecté d'erreur et lorsque vous lancez l'exécution, vous n'avez pas le message : Erreur de script. Le problème, c’est que ce que vous fournit le programme n’est pas conforme à votre attente. Vous avez commis une erreur en écrivant le programme ! Vous essayez donc, dans un premier temps, de relire ce que vous avez écrit pour essayer de comprendre pourquoi cela ne marche pas. Au bout d'un certain temps de réflexion, vous vous rendez à l'évidence, vous n'arrivez pas à comprendre pourquoi cela ne marche pas. Nous allons donc étudier, dans ce paragraphe, des moyens dont nous disposons pour faciliter la recherche de l'erreur.
Introduction d'une variable espion
modifierNous avons d’abord une technique simple qui consiste à introduire dans le programme une variable supplémentaire, que l’on appellera rapport par exemple, dans laquelle vous allez, en certains points du programme, concaténer le contenu d'autres variables. À la fin de la fonction, au lieu de retourner la variable prévue, on retournera la variable rapport qui nous fournira ainsi une information sur le contenu des variables en certains points du programme et nous permettra de localiser plus précisément dans quelle partie se trouve l'erreur. Une fois que nous avons localisé de façon plus précise la partie du programme défaillante, nous pouvons recommencer en concaténant, dans notre variable rapport, plus d'informations sur la partie fautive. Et ainsi de suite jusqu'à repérer l'instruction qui est la cause de nos soucis.
Console de débogage
modifierLorsque nous sommes en mode modification dans un module, nous avons vu que nous avions un certain nombre de possibilités. Si nous continuons à descendre dans la page, tout en bas, nous découvrons un encadré noté Console de débogage représenté ci-dessous :
Nous allons étudier comment cela fonctionne.
Nous commencerons avec le premier exemple dans le premier chapitre, c'est-à-dire la fonction p.Salutation dans le Module:Exemple simple.
Après s'être mis en modification et être descendu jusqu'à la console de débogage, taper à l'intérieur de celle-ci :
=p.Salutation()
puis appuyer sur la touche « Entrée ». Nous voyons alors que ce que l’on a écrit remonte au-dessus de la zone grisée. Puis après un léger temps d'attente apparaît, toujours au-dessus de la zone grisée : « Coucou, c’est moi ! »
Nous avons donc pu tester notre programme. À ce niveau, si quelque chose s'était mal passé, nous aurions eu un message d'erreur nous indiquant la nature de l'erreur et la ligne où l'erreur s'est produite.
Si nous avions tapé :
=p.Salutation
sans les accolades, nous aurions eu comme réponse : function
. Nous pouvons avoir ainsi la nature des variables se trouvant dans le programme.
On aurait pu aussi taper :
print(p.Salutation())
ce qui est équivalent à =p.Salutation()
.
On peut même se servir de la console de débogage comme calculatrice. En effet, si l’on rentre :
=2+3
Elle nous répond 5
.
Faisons maintenant une petite expérience et rajoutons la ligne : mw.log("Il fait beau !")
dans notre programme ainsi :
local p = {}
function p.Salutation()
mw.log("Il fait beau !")
return "Coucou, c’est moi !"
end
return p
Dans la console de débogage tapons à nouveau :
=p.Salutation()
Après nous avoir prévenu que nous avons modifié le programme nous obtenons :
- Il fait beau !
- Coucou, c’est moi !
mw.log
est une commande qui nous permet de transmettre des messages à la console de débogage.
L'intérêt de la fonction mw.log
sur l'instruction return
est que la fonction mw.log
ne nous fait pas sortir du programme comme return
lorsqu'elle est utilisée. On va donc pouvoir utiliser la fonction mw.log
en plusieurs points du programme pour ramener plusieurs informations visibles sur la console de débogage. On peut ainsi construire tout un rapport d'exécution du programme qui apparaîtra sur la console de débogage et nous permettra ainsi de mettre au point le programme.
Ci-dessous, nous représentons la console de débogage après avoir tapé toutes les opérations décrites ci-dessus :
Le programme précédent était simple à étudier car c’était un programme sans paramètre.
Compliquons un peu les choses en étudiant maintenant la fonction p.traduit
se trouvant dans le Module:Autre exemple.
Cette fonction, pour fonctionner, doit recevoir en argument un jour de la semaine. Pour parvenir à transmettre cet argument, nous devons taper dans la console de débogage :
frame = mw.getCurrentFrame()
puis « Entrée ». Le message tapé remonte au dessus de la zone grisée. Nous tapons ensuite :
newFrame = frame:newChild{ args = { 'Jeudi' }}
puis « Entrée ». Le second message tapé remonte au dessus de la zone grisée. Nous tapons alors :
=p.traduit( newFrame )
Puis « Entrée ». Le troisième message tapé remonte au dessus de la zone grisée mais, cette fois, apparaît en plus :
- Thursday
Qui est bien la traduction de jeudi en anglais.
Ci-dessous, nous représentons la console de débogage après avoir tapé toutes les opérations décrites ci-dessus :
Attention, la série des deux commandes :
frame = mw.getCurrentFrame()
newFrame = frame:newChild{ args = { 'Jeudi' }}
doit être retapée si vous modifiez le programme pour faire des essais (introduction d'une fonction mw.log
par exemple).
Compliquons encore les choses en essayant de faire une simulation de débogage de la fonction p.alerte2
se trouvant dans le Module:Balance.
Supposons que le programme ne marche pas (c'est pas vrai ! mais on fait semblant). Pour essayer de comprendre pourquoi le programme ne marche pas, nous allons visualiser sur la console de débogage le contenu de toutes les variables se trouvant dans le programme (en fait, ici, il n'y en a que deux).
Nous utiliserons donc la fonction mw.log
à deux endroits différents pour visualiser les contenus des variables poids et reponse. Le programme sera ainsi complété :
local p = {}
function p.alerte2(frame)
local poids = tonumber(frame.args[1])
mw.log("Le poids rentré est ", poids)
local reponse = "Votre poids est acceptable"
if poids > 54 then
reponse = "Attention, vous commencez à grossir !"
end
mw.log("Le contenu de la variable reponse est : ", reponse)
return reponse
end
return p
Dans la console de débogage, nous taperons successivement les trois commandes :
frame = mw.getCurrentFrame()
newFrame = frame:newChild{ args = { '55' }}
print(p.alerte2( newFrame ))
Après ces opérations, la console de débogage se présente ainsi :
où nous voyons clairement apparaître le contenu des variables poids et reponse, ce qui nous permettra éventuellement de mieux comprendre d'où provient l'erreur (s'il y en avait une).
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
modifierAu premier chapitre, nous avons effleuré la définition d'une table lorsque nous avons étudié 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 suivi de la position entre crochets 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.
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
modifierNous 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)
local jour = frame.args[1]
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é-remplie. Nous sommes obligés de bien préciser à quel indice correspond quelle information.
Si, dans un programme, nous sommes amenés à 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)
local jour = frame.args[1]
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
modifierOn 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
On obtiendra une erreur de script.
Fonctions relatives aux tables
modifierDans 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ères, 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
modifierFonctions appelées par une fonction
modifierNous avons déjà bien étudié les fonctions. Mais toutes les fonctions que nous avons vues jusqu'à maintenant étaient placées d'office dans une table que nous avons appelée 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 />"
return reponse
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
modifierLes 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
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 entrevue à 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)
Et ainsi de suite !!
Fonctions récursives
modifierEn 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
modifierEn 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ée 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.
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ée 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
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
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 à profit 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
reponse = Aj[index](reponse)
return reponse
end
En écrivant : {{#invoke:Ajout|incremente|47|3}}, nous obtenons : 50
Fonctions ayant une table pour argument
modifierUne 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é :
local p = {}
function val(x) -- x est sensé être un nombre
x = x + 3 -- On essaye d'incrémenter 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émenter 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
modifierNous avons, jusqu'à présent toujours déclaré les variables locales 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.
Structures de contrôle
Les structures de contrôle 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é.
Celles qui comportent une répétition sont appelées « boucles ».
La structure if..then..else
modifierNous 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 structures 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 !"
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
modifierLa 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 :
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 devenue fausse et par conséquent nous sommes sortis de la boucle avec reponse contenant la valeur 15.
La structure repeat..until
modifierLa 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
return reponse
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 sortis 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
modifierNous 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
modifierLa 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ée, chaque fois, de la valeur de p.
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 :
local p = {}
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 utilisé 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
modifierCette 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.
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 nil comme 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 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
end
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
modifierNous 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
Par exemple :
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
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 tests et 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
return "La condition est fausse"
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
modifierSoit 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
modifierSoit 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 :
a | b | c |
false | false | false |
false | true | true |
true | false | true |
true | true | true |
L'opérateur not
modifierSoit 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
Par contre, si nous écrivons :
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
modifierNous 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
modifier- ↑ Si l'utilisatrice est en fait un utilisateur homme, il faut remplacer « Grosse vache ! » par « Gros patapouf ! ». Notons cependant que cela ne change rien à la logique de la programmation.
Fonctions basiques
Au paragraphe précédent, nous avons étudié les fonctions. Si nous créons une fonction, c’est pour effectuer généralement un travail répétitif. Plutôt que de répéter une même séquence d'instructions dans plusieurs parties d'un programme, nous créons une fonction contenant cette séquence et nous utilisons cette fonction en différentes parties du programme. Sur ce principe, le Lua met à la disposition des utilisateurs un certain nombre de fonctions pré-créées. Les concepteurs du Lua ont remarqué que certains besoins se faisaient souvent sentir dans l'écriture d'un programme et ont donc préprogrammé un certain nombre de fonctions que nous allons commencer à étudier dans ce chapitre. Dans ce chapitre, nous verrons certaines des fonctions préprogrammées les plus basiques, qui ne sont pas spécialisées dans un domaine particulier. Dans les chapitres suivants, nous verrons des fonctions plus spécialisées dans un domaine particulier comme les mathématiques ou les chaînes de caractères.
L'ordre dans lequel nous étudierons les différentes fonctions ne sera pas alphabétique. Nous essaierons d'adopter un ordre pédagogique en étudiant les fonctions dans l’ordre de difficulté et aussi pratique en étudiant d’abord les fonctions utiles pour mieux comprendre les suivantes.
Tous les exemples de ce chapitre se trouve dans le Module:Basique
type
modifierLa fonction type permet de connaître le type d'objet que contient une variable en retournant l'une des chaîne de caractères suivantes : "nil", "number", "string", "boolean", "table", et "function". Nous avons étudier chacun de ces types dans les chapitres précédent :
- "nil" : c’est en quelque sorte ce que contient une variable par défaut si elle n’est pas affectée. C'est aussi ce que retourne certaines fonctions lorsque quelque chose s'est mal passé.
- "number" : indique que la variable contient un nombre.
- "string" : indique que la variable contient une chaîne de caractères.
- "boolean" : indique que la variable contient un booléen : true ou false (vraie ou faux).
- "table" : indique que la variable contient une table.
- "function" : indique que la variable contient une fonction.
À titre d'exemple, dans un Module:Basique, nous écrirons une fonction toutype qui retourne tous les types possibles en fabriquant une variable qui contiendra successivement tous les types possibles et qui sera, à chaque fois, testée par la fonction type.
local p = {}
function f(x)
return x^2
end
function p.toutype()
local reponse = " "
reponse = reponse..type(cameleon).." " -- La variable cameleon n'a pas été affectée donc la fonction type devrait retourner "nil"
cameleon = 7
reponse = reponse..type(cameleon).." "
cameleon = "miam miam"
reponse = reponse..type(cameleon).." "
cameleon = true
reponse = reponse..type(cameleon).." "
cameleon = p -- N'oublions pas que p a été déclaré comme étant une table en début de module
reponse = reponse..type(cameleon).." "
cameleon = f -- f est la fonction défini un peu plus haut et qui élève un nombre au carré
reponse = reponse..type(cameleon)
return reponse
end
return p
{{#invoke:Basique|toutype}} nous retourne : nil number string boolean table function
tonumber
modifierNous connaissons déjà, un peu, cette fonction, car nous avons dû l'introduire dès le premier chapitre, compte tenu de son utilité. Cette fonction tente de convertir une chaîne de caractères en nombre. Par exemple, elle convertira la chaîne de caractères "23" en nombre 23. S'il n’est pas possible de convertir la chaîne de caractère en nombre, la fonction retournera nil.
À titre d'exemple, dans le Module:Basique, nous écrirons une fonction nombre qui indique à l'utilisateur si l'argument rentré est une chaîne convertible en nombre.
local p = {}
function p.nombre(frame)
local n = tonumber(frame.args[1])
if n == nil then
return "Je n'ai pas réussi à convertir votre chaine de caractères en nombre"
else
return "Vous avez bien rentré un nombre"
end
end
return p
{{#invoke:Basique|nombre|27}} nous retourne : Vous avez bien rentré un nombre
27 est bien un nombre !
{{#invoke:Basique|nombre|Maison}} nous retourne : Je n'ai pas réussi à convertir votre chaine de caractères en nombre
Une maison n’est pas un nombre !
{{#invoke:Basique|nombre|vingt-sept}} nous retourne : Je n'ai pas réussi à convertir votre chaine de caractères en nombre
La fonction tonumber ne connait pas le français !
{{#invoke:Basique|nombre|eleven}} nous retourne : Je n'ai pas réussi à convertir votre chaine de caractères en nombre
Elle ne connait pas l'anglais non plus !
{{#invoke:Basique|nombre|3.14}} nous retourne : Vous avez bien rentré un nombre
La fonction tonumber reconnait le point décimal !
{{#invoke:Basique|nombre|3,14}} nous retourne : Je n'ai pas réussi à convertir votre chaine de caractères en nombre
Mais elle ne reconnait pas la virgule !
{{#invoke:Basique|nombre|7.5698e-3}} nous retourne : Vous avez bien rentré un nombre
Elle reconnait la notation scientifique !
tostring
modifierLa fonction tostring est un peu le contraire de la fonction tonumber, car elle convertit son argument en chaîne de caractères.
Comme exemple, nous allons reprendre l'exemple donné pour la fonction type, mais en remplaçant type par tostring :
local p = {}
function f(x)
return x^2
end
function p.verschaine()
local reponse = " "
reponse = reponse..tostring(cameleon).." " -- La variable cameleon n'a pas été affectée donc la fonction type devrait retourner "nil"
cameleon = 7
reponse = reponse..tostring(cameleon).." "
cameleon = "mian mian"
reponse = reponse..tostring(cameleon).." "
cameleon = true
reponse = reponse..tostring(cameleon).." "
cameleon = p -- N'oublions pas que p a été déclaré comme étant une table en début de module
reponse = reponse..tostring(cameleon).." "
cameleon = f -- f est la fonction défini un peu plus haut et qui élève un nombre au carré
reponse = reponse..tostring(cameleon)
return reponse
end
return p
{{#invoke:Basique|verschaine}} nous retourne : nil 7 mian mian true table function
Lorsque l’objet est nil, une table ou une fonction, la fonction tostring nous délivre le même message que la fonction type. Lorsque l’objet est une chaîne de caractères, tostring nous rend la chaîne à l'identique. Lorsque l’objet est un nombre, ce nombre est transformé en chaîne en rajoutant des guillemets (7 devient "7"). Lorsque l’objet est un booléen, tostring nous délivre "true" ou "false" selon le cas.
error
modifierC'est une fonction qui provoque volontairement une erreur de script, mais avec un message d'erreur que l’on a choisi et un contrôle sur la position de l'erreur indiquée par le Lua.
La syntaxe de cette fonction est error(message, niveau). message est le message d'erreur apparaissant lorsque l’on clique sur l'erreur de script(en rouge).
Pour niveau :
S'il y a 0, on n'aura pas de message indiquant la ligne ou la fonction error se trouve.
S'il y a 1 ou rien, on aura un message indiquant la ligne ou la fonction error concernée se trouve.
S'il y a 2, on aura un message indiquant la ligne ou se fait l'appel à la fonction contenant la fonction error concernée.
S'il y a 3, on aura un message indiquant la ligne ou se fait l'appel à une fonction qui a appelé la fonction contenant la fonction error concernée.
Et ainsi de suite..
Prenons un exemple :
local p = {}
function i(x)
error("turlututu chapeau pointu",3)
end
function h(x)
i(x)
end
function g(x)
h(x)
end
function p.erreur()
g(2)
end
return p
{{#invoke:Basique|erreur}} nous retourne : Erreur Lua dans Module:Basique à la ligne 103 : turlututu chapeau pointu.
En cliquant sur l'erreur de script si dessus, nous obtenons :
Erreur Lua dans Module:Basique à la ligne n:
turlututu chapeau pointu.
Dans la pile des appels, nous voyons que la ligne n (voir la valeur de n en cliquant sur l'erreur de script ci-dessus) correspond à la ligne où a été déclarée la fonction g. la fonction g appelant la fonction h qui appelle la fonction i ou nous avons mis la fonction error, nous sommes bien en conformité avec le fait que nous avons mis 3 comme niveau de la fonction error.
pcall
modifierLa fonction pcall permet d'appeler une fonction en gérant une éventuelle erreur lors de son exécution.
Dans l'exemple ci-dessous, la fonction p.crash appelle avec pcall une fonction dans laquelle, on a mis volontairement une instruction qui, normalement devrait provoquer une erreur de script. Nous allons voir que nous n'avons pas le fameux message rouge indiquant l'erreur de script car la fonction pcall gère elle-même l'erreur de script. Le premier argument de la fonction pcall est le nom de la fonction appelée, les arguments suivants sont les arguments de la fonction appelée.
local p = {}
function plouf(x)
local tab ={6,8}
return x + tab
end
function p.crash()
local a,b = pcall(plouf,3)
if a == false then
return b
else
return "Aucun probléme ! La fonction a retourné"..b
end
end
return p
{{#invoke:Basique|crash}} nous retourne : Module:Basique:112: attempt to perform arithmetic on local 'tab' (a table value)
Dans le programme précédent, la fonction pcall retourne deux valeurs (si la fonction appelée retournait n valeurs, la fonction p.call retournerait n + 1 valeurs).
Dans notre exemple, la fonction p.call appelle la fonction plouf dans laquelle, nous avons mis volontairement une erreur de script (nous tentons l'addition d'un nombre avec une table). Au lieu d’avoir notre habituel message rouge indiquant l'erreur de script, la fonction pcall va retourner en première valeur (a dans notre exemple), la valeur false et en seconde valeur (b dans notre exemple) un message indiquant la ligne ou s'est produite l'erreur de script et la nature de l'erreur de script (voir l'exécution de notre exemple).
S'il n'y avait pas eut d'erreur de script dans la fonction plouf de notre exemple, la fonction p.call aurait retourné en première valeur, la valeur true et en seconde valeur la valeur retournée normalement par la fonction plouf. Si la fonction plouf retournait plusieurs valeurs, la fonction pcall retournerait aussi toutes ces valeurs à la suite de la valeur true.
Voir un autre exemple d'utilisation de la fonction pcall dans le chapitre : Gestion de l'environnement, paragraphe : Gestion des erreurs (troisième sous-paragraphe).
next
modifierNous avons vu que les clés des tables pouvaient aussi bien être des nombres que des chaînes de caractères. Dans d'autres langages, l'accès aux tables se fait uniquement avec des nombres et l’on à vu que si l’on souhaitait faire quelque chose sur une table indexée par des nombres, on pouvait le faire facilement en utilisant une boucle for..do. Là où les choses deviennent plus compliquées, c’est quand l’on souhaite travailler sur une table indexée par des chaînes de caractères. Le programmeur habitué, dans un autre langage, aux tables indexées par des nombres, a de quoi être décontenancé. Comment parcourir une table indexée par des chaînes de caractères. C'est ici, qu'intervient la fonction next. Cette fonction a la capacité de nous donner l'index suivant un index donné dans une table ainsi que l’objet correspondant à cet index (cette fonction retourne deux objets). Par exemple, si nous écrivons next(tab, "bouillotte"), tab étant une table et "bouillotte", une chaîne de caractères servant d'index dans la table tab, alors cette instruction nous donnera l'index suivant dans la table et l’objet correspondant ("marmite" par exemple et ce qu’il y a dans la marmitte). Il suffira alors d'écrire : next(tab, "marmite") pour avoir, à nouveau, l'index suivant avec l’objet correspondant et ainsi de suite. On pourra de cette façon parcourir toute la table !
Nous devons préciser deux petits détails :
Si nous ne savons pas quel est le premier index, nous taperons simplement next(tab, nil) et la fonction next nous retournera le premier index et l’objet correspondant. On peut aussi taper simplement next(tab) pour avoir le premier index et l’objet correspondant.
Si "bouillotte" est le dernier index de la table, alors next(tab, "bouillotte") nous retournera nil
Nous devons bien noter ces deux petits détails, car ils peuvent être causes de bien des soucis dans un programme.
À titre d'exemple, nous commencerons par écrire une fonction listecle qui se contentera de retourner tous les index d'une table que l’on aura rentrée avant la fonction ainsi que les objets correspondants:
local Categorie = { ["Prénom"] = "Christine", ["Mois"] = "Avril", ["Métier"] = "Boulanger", ["Poisson"] = "Truite", ["Métal"] = "Argent", ["Planète"] = "Saturne", ["Instrument"] = "Piano"}
function p.listecle()
local reponse = " "
local suivant, objet
for index = 1, 7 do
suivant,objet = next(Categorie,suivant)
reponse = reponse.."<br> "..suivant.." correspond à "..objet
end
return reponse
end
{{#invoke:Basique|listecle}} nous retourne :
Prénom correspond à Christine
Instrument correspond à Piano
Planète correspond à Saturne
Métal correspond à Argent
Mois correspond à Avril
Poisson correspond à Truite
Métier correspond à Boulanger
La première remarque que l’on peut faire est que les index ne sont pas sortis dans l’ordre où nous les avons écrit ??? Qu'importe, du moment où on les a tous !!! (C'est même pas l’ordre alphabétique d'ailleurs !?)
L'instruction intéressante dans ce programme est l'instruction :
suivant,objet = next(Categorie,suivant)
qui, à chaque boucle, remplace l'index contenu dans la variable suivant par l'index suivant et donne aussi l’objet correspondant.
Le programme précédent est un peu simpliste car il supposait que nous connaissions déjà le contenu de la table et qu’il y avait exactement 7 index. C'est pour cela que nous avons utilisé une boucle for..do. Si l’on remplace dans la programme précédent le 7 de la boucle for..do par un 8, alors nous aurons une belle erreur de script à cause de l'instruction :
reponse = reponse.."<br> "..suivant.." correspond à "..objet
car suivant contiendra nil à la huitième boucle
Si nous ne connaissons pas la table d'avance et que nous ne savons pas combien elle contient de clés, nous devons la parcourir avec une autre instruction de contrôle capable de détecter le nil apparaissant dans la variable suivant lorsque l’on a fini de parcourir la table.
nous allons donc écrire une fonction yatil, moins lamentable que la précédente, en utilisant une boucle repeat..until qui va parcourir la table jusqu'à ce que nil apparaisse dans suivant. Ce programme a pour but de nous indiquer si un mot rentré en paramètre se trouve dans la table :
local Categorie = { ["Prénom"] = "Christine", ["Mois"] = "Avril", ["Métier"] = "Boulanger", ["Poisson"] = "Truite", ["Métal"] = "Argent", ["Planète"] = "Saturne", ["Instrument"] = "Piano"}
function p.yatil(frame)
local suivant,objet
local trouve = false
repeat
suivant,objet = next(Categorie,suivant) --Cherche la clé suivante et la met dans suivant
if objet == frame.args[1] then trouve = true end
until suivant == nil or trouve -- On tourne dans la boucle jusqu'à ce que toute la table ait été parcourue ou jusqu'à ce qu'on ait trouvé
if trouve then
return "Le mot figure dans la table"
else
return "Le mot ne figure pas dans la table"
end
end
Nota : Le programme ci-dessus est volontairement mal écrit. Une amélioration de celui-ci est proposée dans l'exercice 9-2.
{{#invoke:Basique|yatil|Truite}} nous retourne : Le mot figure dans la table
{{#invoke:Basique|yatil|Trottinette}} nous retourne : Le mot ne figure pas dans la table
Astuce Nous avons vu que next(tab) nous fourni la première clé de la table. Si next(tab) nous retourne nil, cela signifie qu’il n'y a pas de première clé dans la table, autrement dit que la table est vide. next(tab) permet donc aussi de tester si une table est vide. |
ipairs
modifierLa fonction ipairs nous fourni une façon plus évolué et surtout plus commode de parcourir une table avec clé numérique. Un exemple simple vaut mieux qu'un long discours et va nous permettre de mieux comprendre. Écrivons donc une fonction description qui se contente de dire bêtement ce que l’on trouve à la clé numéro i (i étant un nombre) :
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
{{#invoke:Basique|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.
Si la table contient, à la fois, des clés numériques et des clés sous forme de chaîne de caractère, la fonction ipairs parcourra les clés numériques en ignorant les clés sous forme de chaîne de caractères.
pairs
modifierLa fonction pairs réalise à peu près la même chose que la fonction ipairs mais en considérant, cette fois, les tables dont les clés sont des chaînes de caractères. Nous prendrons donc, là aussi, un exemple pour mieux comprendre. Exemple, d'ailleurs très similaire à l'exemple choisi dans le paragraphe précédent :
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
{{#invoke:Basique|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.
Nous voyons que, cette fois, la table n’est pas lue dans l’ordre où on l'a écrit, ni dans l’ordre alphabétique.
Si la table contient, à la fois, des clés numériques et des clés sous forme de chaîne de caractère, la fonction pairs parcourra la totalité des clés, qu’elles soient sous forme numérique ou sous forme de chaîne de caractères.
unpack
modifierNous avons vu dans les chapitres précédents que l’on est souvent amené à présenter des données séparées par des virgules (paramètres d'une fonction, valeurs retournées par une fonction, remplissage d'une table indexée numériquement, etc.)
Si les données, que l’on veut séparer par des virgules sont rangées dans une table indexée numériquement, on peut les sortir et les présenter séparées par des virgules grâce à la fonction unpack.
unpack(table,i,j) sort les données d'une table indexée numériquement, de l'index i à l'index j et les présente séparées par des virgules.
Par exemple dans :
local nombres_premiers = {2,3,5,7,11,13,17,19,23,29,31,37,41}
function p.Calcul()
local premier = {unpack(nombres_premiers,5,8)}
etc.
end
La fonction p.calcul n'avait besoin que des nombres premiers entre 10 et 20. Elle a donc créer une table premier qui ne contient que les nombres premiers entre 10 et 20 extrait, grâce à la fonction unpack, de la table nombres_premiers. Les nombres premiers extraits ce trouvaient, dans la table nombres_premiers, de l'index 5 à l'index 8.
Nous ne sommes pas obligé de préciser les valeurs de i et j à l'appel de la fonction unpack. Par défaut i vaut 1 et j est égal au nombre d'éléments se trouvant dans la table.
select
modifierLa fonction select considère son premier argument et agit sur le reste de ses arguments en fonction du premier.
- Si le premier argument est 1, la fonction select retourne le reste de ses arguments. Par exemple select(1,a,b,c,d) retournera a,b,c,d
- Si le premier argument est 2, la fonction select retourne le reste de ses arguments sauf celui qui suit l'argument égal à 2. Par exemple select(2,a,b,c,d) retournera b,c,d
- Et ainsi de suite. Par exemple select(3,a,b,c,d,e,f) retournera c,d,e,f. select(4,a,b,c,d,e) retournera d,e
- Si le premier argument est #, la fonction select retournera le nombre de ses arguments sans compter le premier qui est #. Par exemple select(#,a,b,c,d,e,f,g,h) retournera 8
rawget
modifierCette fonction à deux paramètres est telle que rawget(tab,k) est équivalent à tab[k] (tab étant une table et k une clé). La seule différence est que la fonction rawget ignore une éventuelle méta-méthode associée au champ __index. Pour plus de précisions voir le chapitre sur les méta-tables
rawequal
modifierCette fonction à deux paramètres est telle que rawequal(a,b) est équivalent à a == b (a et b étant des tables). La seule différence est que la fonction rawequal ignore une éventuelle méta-méthode associée au champ __eq. Pour plus de précisions voir le chapitre sur les méta-tables
rawset
modifierCette fonction à trois paramètres est telle que rawset(tab,k,v) est équivalent à tab[k] = v (tab étant une table, k une clé et v une valeur). La seule différence est que la fonction rawset ignore une éventuelle méta-méthode associée au champ __newindex. Pour plus de précisions voir le chapitre sur les méta-tables
Chaînes de caractères
Nous allons, dans ce chapitre, étudier quelques fonctions préprogrammées permettant de faire quelques opérations sur les chaînes de caractères. Comme dans le chapitre précédent, nous ne rangerons pas nécessairement les fonctions dans l’ordre alphabétique.
Tous les exemples de ce chapitre seront rangés dans le Module:Chaine.
Généralités
modifierUne chaîne de caractères se représente par un texte entouré de guillemet " ". L'affectation à une variable se présente ainsi :
phrase = "Je pense donc je suis"
Il est possible de connaître le nombre de caractères utilisés dans la chaîne de caractères en utilisant l'opérateur #
longueur = #"Je pense donc je suis"
longueur sera alors de type number et contiendra le nombre 21.
On peut aussi utiliser cet opérateur sur une variable de type string pour connaître la longueur de la chaîne de caractères qu'elle contient :
longueur = #phrase
Le caractère # ne marche pas sur l'unicode. Pour l'unicode, on utilisera la fonction mw.ustring.len |
Sans rien y mettre, on peut initialiser une variable comme étant de type string en écrivant :
local mot = ""
On rappelle l’utilisation de l'opérateur .. permettant de concaténer deux chaînes de caractères.
phrase = "Je pense donc je suis"..", mais que suis-je ?"
La variable phrase contiendra alors la chaîne de caractères : "Je pense donc je suis, mais que suis-je ?"
La librairie String
modifierLa librairie String est constituée d'un ensemble de fonctions rangées dans une table nommée string. Nous commencerons donc par visualiser le contenu de cette table grâce au programme suivant :
local p = {}
function p.visuString(frame)
reponse = ""
for index, objet in pairs(string) do
reponse = reponse.."<br />À la clé '''"..index.."''', on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Chaine|visuString}} nous donne :
À la clé sub, on trouve un objet de type : function
À la clé find, on trouve un objet de type : function
À la clé gsub, on trouve un objet de type : function
À la clé gmatch, on trouve un objet de type : function
À la clé gfind, on trouve un objet de type : function
À la clé byte, on trouve un objet de type : function
À la clé rep, on trouve un objet de type : function
À la clé match, on trouve un objet de type : function
À la clé uupper, on trouve un objet de type : function
À la clé ulower, on trouve un objet de type : function
À la clé reverse, on trouve un objet de type : function
À la clé upper, on trouve un objet de type : function
À la clé len, on trouve un objet de type : function
À la clé format, on trouve un objet de type : function
À la clé char, on trouve un objet de type : function
À la clé lower, on trouve un objet de type : function
Nous étudions ci-dessous chacune des fonctions en détail :
string.sub
modifierCette fonction permet d'extraire une sous-chaîne d'une chaîne donnée. Elle admet trois paramètre. Le premier est la chaîne dont on veut extraire une sous-chaîne. Le deuxième est la position du premier caractère extrait. Le troisième est la position du dernier caractère extrait. Par exemple : string.sub("abcdefghijkl",3,7) devrait nous retourner "cdefg". Il est possible de donner les positions avec des nombres négatifs, cela signifie que l’on compte la position à partir de la fin de la chaîne. Si le troisième paramètre est absent, cela signifie que l’on extrait jusqu'au dernier caractère de la chaîne.
Pour tester cette fonction, nous utiliserons une fonction p.extrait ainsi écrite :
function p.extrait(frame)
phrase = frame.args[1]
debut = tonumber(frame.args[2])
fin = tonumber(frame.args[3])
return string.sub(phrase,debut,fin)
end
{{#invoke:Chaine|extrait|abcdefghijkl|3|7}} nous donne : cdefg
{{#invoke:Chaine|extrait|abcdefghijkl|-5|-2}} nous donne : hijk
En Unicode, on utilisera la fonction : mw.ustring.sub.
string.find
modifierCette fonction permet de trouver un motif particulier dans une chaîne de caractères et de retourner sa position.
Elle s'utilise sous la forme string.find(chaîne, motif, position de départ de la recherche,true)
Si la fonction ne trouve pas le motif, elle retourne nil
La position de départ est optionnelle. Si on ne la met pas, la recherche démarrera au début (dans ce cas true ne devra pas être mis non plus).
Si l’on ne met pas true en quatrième paramètre, certains caractères de la chaîne motif vont être interprétés de façon particulière. Ces caractères sont "(", ")", "&", "%". On devra donc mettre true si le motif que l’on recherche contient ces caractères sans qu’ils aient un sens particulier. Si, par exemple, on veut chercher "groupe (mathématiques)" dans un texte qui contient cette expression, on devra écrire : string.find(texte, "groupe (mathématiques)", 1,true). Si l’on se contente d'écrire : string.find(texte, "groupe (mathématiques)"), on obtiendra nil.
Écrivons une fonction p.cherche qui cherche un mot dans la phrase "Rien ne sert de courir, il faut partir à point" :
function p.cherche(frame)
local mot = frame.args[1]
local phrase = "Rien ne sert de courir,il faut partir à point"
local position = string.find(phrase,mot)
if position then
return "Le mot recherché se trouve à la position : "..position
else
return "Je n'ai pas trouvé !"
end
end
{{#invoke:Chaine|cherche|courir}} nous donne : Le mot recherché se trouve à la position : 17
Le fait de pouvoir démarrer la recherche à une position particulière peut être utile si on cherche un mot dans un paragraphe d'une page. On commence alors par chercher la position du titre du paragraphe et on cherche ensuite le mot à partir de cette position.
En Unicode, on utilisera la fonction : mw.ustring.find.
string.gsub
modifierCette fonction permet de remplacer toutes les occurrences d'un motif donné par un autre motif. Par exemple, cette fonction permet de remplacer un mot d'une phrase par un autre mot. string.gsub("Je casse un œuf dur", "casse", "mange") nous retourne : Je mange un œuf dur,1.
Nous voyons aussi que, en plus de la phrase modifiée, cette fonction nous retourne le nombre de modifications faites.
Écrivons une fonction p.oeuf pour vérifier ce que l’on vient de dire :
function p.oeuf()
local phrase,nombre = string.gsub("Je casse un œuf dur", "casse", "mange")
return phrase.."(Le nombre de mots remplacés est "..nombre..")"
end
{{#invoke:Chaine|oeuf}} nous donne : Je mange un œuf dur(Le nombre de mots remplacés est 1)
En Unicode, on utilisera la fonction : mw.ustring.gsub.
string.gmatch
modifierEn Unicode, on utilisera la fonction : mw.ustring.gmatch
string.gfind
modifier
string.byte
modifierEn Unicode, on utilisera la fonction : mw.ustring.byte ou la fonction : mw.ustring.codepoint
string.rep
modifierCette fonction répète une chaîne de caractères un certain nombre de fois. Elle a donc deux arguments. Le premier argument est la chaîne de caractères à répéter et le second argument est le nombre de fois que l’on doit répéter cette chaîne. Par exemple, string.rep("cou",2) devrait nous donner "coucou". À titre d'exemple, nous écrirons une fonction p.echo qui répétera une chaîne de caractères un nombre de fois que l’on précisera :
function p.echo(frame)
local mot = frame.args[1]
local nombre = tonumber(frame.args[2])
return string.rep(mot,nombre)
end
{{#invoke:Chaine|echo|glou |3}} nous donne : glou glou glou
En Unicode, on utilisera la fonction : mw.ustring.rep
string.match
modifierEn Unicode, on utilisera la fonction : mw.ustring.match
string.uupper
modifier
string.ulower
modifier
string.reverse
modifierCette fonction renverse la chaîne de caractères qui lui est soumise. La dernière lettre devient la première, l'avant dernière devient la seconde et ainsi de suite. Par exemple : "coquillage" devient "egalliuqoc". Pour tester cette fonction, la première idée qui vient à l'esprit est d'écrire une fonction p.palindrome qui teste si un mot entré en argument est un palindrome :
function p.palindrome(frame)
local mot = frame.args[1]
if mot == string.reverse(mot) then
return mot.." est un palindrome."
else
return mot.." n’est pas un palindrome."
end
end
{{#invoke:Chaine|palindrome|radar}} nous donne : radar est un palindrome.
{{#invoke:Chaine|palindrome|renier}} nous donne : renier n’est pas un palindrome.
string.upper
modifierCette fonction permet de remplacer les caractères minuscules de la chaîne de caractères par les caractères majuscules correspondants. Par exemple, "crocodile" deviendra "CROCODILE". Essayons de voir si ça marche ! Dans le Module:Chaine, écrivons une fonction p.capitale qui utilise directement la fonction string.upper sur une chaîne de caractères rentrée en argument :
function p.capitale(frame)
local mot = frame.args[1]
return string.upper(mot)
end
{{#invoke:Chaine|capitale|crocodile}} nous donne : CROCODILE
{{#invoke:Chaine|capitale|pIScinE eT 3 pApiLloNs}} nous donne : PISCINE ET 3 PAPILLONS
Nous constatons sur le dernier exemple que les majuscules restent majuscules. Les autres caractères comme les chiffres ne sont pas modifiés.
En Unicode, on utilisera la fonction : mw.ustring.upper
string.len
modifierCette fonction nous donne le nombre de caractères contenus dans une chaîne de caractères. Les lettres accentuées comptent double. Par exemple, string.len(Marionnette) devrait nous retourner 11 car il y a 11 lettres dans le mot "Marionnette". Par contre string.len(système) nous retournera 8 à cause de la lettre accentuée è qui compte double. Comme exemple, nous écrirons une fonction p.longueur qui nous renverra le nombre de caractères d'une chaîne de caractères rentrée en argument :
function p.longueur(frame)
local phrase = frame.args[1]
return string.len(phrase)
end
{{#invoke:Chaine|longueur|Marionnette}} nous donne : 11
{{#invoke:Chaine|longueur|La vague déferle sur la plage}} nous donne : 30
Dans le deuxième exemple, nous voyons qu’il y a 29 caractères dans la phrase et pourtant la fonction string.len en compte 30 à cause de la présence de la lettre accentuée é dans la mot déferle
En Unicode, on utilisera la fonction : mw.ustring.len
Pour la fonction mw.ustring.len, les lettres accentuées ne comptent pas double. Cette fonction sera donc préférable pour traiter les chaînes de caractères pouvant contenir des lettres accentuées.
string.format
modifierLa fonction string.format permet d'obtenir un format particulier pour un nombre ou une chaîne de caractères.
Elle s'utilise sous la forme : string.format(option,nombre ou chaîne)
L'option permet de définir le type de format que l’on désire obtenir. Il sera écrit sous la forme : '%lettre'
Nous allons passer en revue, les différentes options possible :
L'option '%c'
Permet d'obtenir le caractère correspondant à un code ASCII particulier. Par exemple :
string.format('%c',87) donnera la lettre majuscule W.
string.format('%c',36) donnera le symbole $.
L'option '%d'
Donne la partie entière d'un nombre
string.format('%d',56.235) donnera 56.
string.format('%d',-23.827) donnera -23.
L'option '%E'
Permet d'obtenir l'écriture scientifique correspondante à un nombre donnée, la puissance de 10 étant écrite avec E.
string.format('%E',0.097) donnera 9.700000E-2.
L'option '%e'
Permet d'obtenir l'écriture scientifique correspondante à un nombre donnée, la puissance de 10 étant écrite avec e.
string.format('%e',0.097) donnera 9.700000e-2.
L'option '%f'
Permet d'obtenir un nombre décimal écrit avec six chiffres après la virgule.
string.format('%f',0.097) donnera 0.097000
string.format('%f',823.43657835) donnera 823.436578
L'option '%g'
Transforme une notation scientifique en notation décimale tout en arrondissant le nombre à six chiffres significatifs.
string.format('%g',1.5789235E4) donnera 15789.2
string.format('%g',-1.5789235e-4) donnera -0.000157892
L'option '%G'
Transforme une notation scientifique en notation décimale tout en arrondissant le nombre à six chiffres significatifs.
string.format('%G',1.5789235E4) donnera 15789.2
string.format('%G',-1.5789235e-4) donnera -0.000157892
L'option '%i'
Donne la partie entière d'un nombre
string.format('%i',56.235) donnera 56.
string.format('%i',-23.827) donnera -23.
L'option '%o'
Cette option permet de traduire un nombre écrit en base 10 en nombre écrit en base 8.
string.format('%o',9) donnera 11
string.format('%o',93) donnera 135
L'option '%u'
Convertit dans un format qui n'accepte que les nombres entiers positifs compris entre 0 et 18446744073709551615. Le nombre obtenu n'est plus à virgule flottante. Pour les nombres positifs, on obtient la partie entière. Pour les nombres négatifs on obtient la somme du nombre négatif avec 18446744073709551616.
string.format('%u',-1) donnera 18446744073709551615
string.format('%u',21.2479) donnera 21
L'option '%X'
Cette option permet de traduire un nombre en hexadécimal. Autrement dit, cette option permet de traduire un nombre écrit en base 10 en nombre écrit en base 16. Les lettres utilisées pour écrire ce nombre seront en majuscule.
string.format('%X',47) donnera 2F
string.format('%X',28) donnera 1C
L'option '%x'
Cette option permet de traduire un nombre en hexadécimal. Autrement dit, cette option permet de traduire un nombre écrit en base 10 en nombre écrit en base 16. Les lettres utilisées pour écrire ce nombre seront en minuscule.
string.format('%x',169) donnera a9
string.format('%x',735) donnera 2df
L'option '%q'
Traite une chaîne de caractère. Les guillemets sont conservés.
string.format('%q',"La voiture roule") donnera : "La voiture roule"
L'option '%s'
Traite une chaîne de caractère. Les guillemets ne sont pas conservés.
string.format('%s',"La voiture roule") donnera : La voiture roule
Nous pouvons retrouver tous les exemples précédents simulés avec la fonction p.formatage dans le Module:Chaine :
function p.formatage()
local reponse = " "
reponse = reponse.."<br>Avec c, on obtent : "..string.format('%c',87).." et "..string.format('%c',36)
reponse = reponse.."<br>Avec d, on obtent : "..string.format('%d',56.235).." et "..string.format('%d',-23.827)
reponse = reponse.."<br>Avec E, on obtent : "..string.format('%E',0.097).." et "..string.format('%E',-2833.24)
reponse = reponse.."<br>Avec e, on obtent : "..string.format('%e',0.097).." et "..string.format('%e',-2833.24)
reponse = reponse.."<br>Avec f, on obtent : "..string.format('%f',0.097).." et "..string.format('%f',823.43657835)
reponse = reponse.."<br>Avec g, on obtent : "..string.format('%g',1.5789235E4).." et "..string.format('%g',-1.5789235e-4)
reponse = reponse.."<br>Avec G, on obtent : "..string.format('%G',1.5789235E4).." et "..string.format('%G',-1.5789235e-4)
reponse = reponse.."<br>Avec i, on obtent : "..string.format('%i',56.235).." et "..string.format('%i',-23.827)
reponse = reponse.."<br>Avec o, on obtent : "..string.format('%o',9).." et "..string.format('%o',93)
reponse = reponse.."<br>Avec u, on obtent : "..string.format('%u',-1).." et "..string.format('%u',21.2479)
reponse = reponse.."<br>Avec X, on obtent : "..string.format('%X',47).." et "..string.format('%X',28)
reponse = reponse.."<br>Avec x, on obtent : "..string.format('%x',169).." et "..string.format('%x',735)
reponse = reponse.."<br>Avec q, on obtent : "..string.format('%q',"La voiture roule")
reponse = reponse.."<br>Avec s, on obtent : "..string.format('%s',"La voiture roule")
return reponse
end
{{#invoke:Chaine|formatage}} nous donne :
Avec c, on obtent : W et $
Avec d, on obtent : 56 et -23
Avec E, on obtent : 9.700000E-02 et -2.833240E+03
Avec e, on obtent : 9.700000e-02 et -2.833240e+03
Avec f, on obtent : 0.097000 et 823.436578
Avec g, on obtent : 15789.2 et -0.000157892
Avec G, on obtent : 15789.2 et -0.000157892
Avec i, on obtent : 56 et -23
Avec o, on obtent : 11 et 135
Avec u, on obtent : 18446744073709551615 et 21
Avec X, on obtent : 2F et 1C
Avec x, on obtent : a9 et 2df
Avec q, on obtent : "La voiture roule"
Avec s, on obtent : La voiture roule
En Unicode, on utilisera la fonction : mw.ustring.format
string.char
modifier
string.lower
modifierCette fonction fait exactement l'inverse de la fonction string.upper, elle transforme les majuscules en minuscules dans une chaîne de caractères. Pour la tester, nous écrirons une fonction p.minuscule ainsi :
function p.minuscule(frame)
local mot = frame.args[1]
return string.lower(mot)
end
{{#invoke:Chaine|minuscule|CORNICHON}} nous donne : cornichon
{{#invoke:Chaine|minuscule|pIScinE eT 3 pApiLloNs}} nous donne : piscine et 3 papillons
Nous constatons sur le dernier exemple que les minuscules reste minuscules. Les autres caractères comme les chiffres ne sont pas modifiés.
En Unicode, on utilisera la fonction : mw.ustring.lower
Motifs (pattern)
modifierEn attendant une rédaction plus détaillée et moins obscure pour les non-programmeurs, cette section est issue d'une recopie d'une partie de la page : mw:Extension:Scribunto/Lua reference manual/fr
En Lua, les motifs sont similaires aux expressions régulières sans pour autant être identiques. Voici quelques différences avec les expressions régulières et les PCRE :
- Le caractère d'échappement est le symbole du pour cent
%
, pas l'antislash\
; - Le point
.
remplace tout caractère, y compris le retour chariot ; - Il n'y a pas d'option pour être insensible à la casse ;
- L'alternative
|
n’est pas définie ; - Les quantificateurs (
*
,?
,+
, et-
) ne peuvent s'appliquer qu’à un caractère ou une classe de caractère, pas à un groupe de capture ; - Le seul quantificateur non possessif est
-
, qui est équivalent au quantificateur*?
de PCRE ; - Pas moyen de quantifier de manière précise (comme le ferait
{n,m}
avec PCRE ; - Les seuls caractères de contrôle qui n'ont aucune profondeur sont
^
et$
(lua n'offre pas la possibilité d’utiliser\b
ou(?=···)
qui apparaisse dans les PCRE).
Voir aussi motifs ustring pour des motifs similaires utilisant cette fois les caractères Unicode.
Classes de caractères :
modifierUne classe de caractères est utilisée pour représenter un ensemble de caractères. Les combinaisons suivantes sont autorisées pour représenter une classe de caractères :
- x : (où x n’est pas un caractère « magique »
^$()%.[]*+-?
) représente le caractère x lui-même. .
: (un point) représente tous les caractères.%a
: représente toutes les lettres (A-Za-z, mais pas é, è, û...).%c
: représente tous les caractères de contrôle.%d
: représente tous les chiffres.%l
: représente toutes les lettres minuscules (a-z, mais pas é, è, û...).%p
: représente tous les signes de ponctuation.%s
: représente tous les caractères séparateurs (espace, saut de ligne, …).%u
: représente toutes les lettres majuscules (A-Z, mais pas É, Ê...).%w
: représente tous les caractères alphanumériques (A-Za-z0-9, mais pas é, è, û...).%x
: représente tous les chiffres hexadécimaux.%z
: représente le caractère\0
(le caractère dont la valeur numérique est nulle).%x
: (où x est n’importe quel caractère non alphanumérique) représente le caractère x. Ceci est la façon habituelle d’utiliser explicitement un caractère « magique » (ayant un sens de contrôle). Tout caractère de ponctuation (même les non magiques) peut être précédé d'un%
quand il représente lui-même.[set]
: représente la classe qui est l'union de tous les caractères présents dans set. On peut indiquer un intervalle de caractère en séparant les caractères qui bornent cet intervalle avec un-
.
Toutes les classes %
x décrites plus haut peuvent aussi être utilisées dans un set.
Tous les autres caractères dans un set représentent eux-mêmes. Par exemple [%w_]
(ou [_%w]
) représente tous les caractères alphanumériques et le tiret bas (underscore), [0-7]
représente les chiffres octaux, et [0-7%l%-]
représente les chiffres octaux plus les lettre minuscules plus le caractère -
. Utiliser à la fois les intervalles et les classes a un effet non spécifié. Ainsi, des motifs comme [%a-z]
ou [a-%%]
n'ont pas de sens.
[^set]
: représente le complément de set (voir l'interprétation de set ci-dessus), c'est-à-dire tous les caractères n'appartenant pas à set.
Pour toutes les classes représentées par une seule lettre (%a
, %c
, …), la lettre majuscule correspondante représente le complément de cette classe.
Par exemple %S
représente tous les caractères qui ne sont pas des séparateurs.
Élément de motif (pattern item) :
modifierUn élément de motif peut être :
- un singleton d'une classe de caractère, qui correspond à un caractère unique dans la classe ;
- un singleton d'une classe de caractère suivi d'un
*
, qui correspond à 0 ou plus répétition(s) de tout caractère de la classe. Cette répétition correspond toujours à la plus longue répétition possible ; - un singleton d'une classe de caractère suivi d'un
+
, qui correspond à 1 ou plus répétition(s) de tout caractère de la classe. Cette répétition correspond toujours à la plus longue répétition possible ; - un singleton d'une classe de caractère suivi d'un
-
, qui correspond également à 0 ou plus répétition(s) de tout caractère de la classe. Contrairement à*
, cette répétition correspond toujours à la plus petite répétition possible ; - un singleton d'une classe de caractère suivi d'un
?
, qui correspond à 0 ou 1 occurrence d'un caractère de la classe ; %n
, pour n compris entre 1 et 9. Cet élément correspond à une sous-chaîne valant la n-ième chaîne capturée (voir plus bas) ;%bxy
, ou x et y sont deux caractères distincts. Cet élément correspond à la chaîne qui commence par x, se termine par y, et où x et y sont équilibrés (balanced). Cela signifie, en lisant la chaîne de gauche à droite, compter +1 pour chaque x et -1 pour chaque y, le y terminal est le premier y pour lequel le compte atteint 0. Par exemple, l'élément%b()
correspond à une expression avec des parenthèses équilibrées.
Motif (pattern) :
modifierUn motif est une séquence d'éléments de motif.
Un ^
au début d'un motif correspond au début de la ligne de la chaîne traitée.
Un $
à la fin du motif correspond à la fin de la ligne de la chaîne traitée.
Aux autres positions, ^
et $
n'ont pas de sens particulier et représentent le caractère qui les constitue.
Captures :
modifierUn motif (pattern) peut contenir des sous-motifs entourés de parenthèses, qui décrivent des captures.
Quand une correspondance réussit, la sous-chaîne de la chaîne qui correspond est stockée (capturée) pour usage ultérieur.
Les captures sont numérotées dans l’ordre de leur parenthèse de gauche.
Par exemple, dans le motif "(a*(.)%w(%s*))"
, la partie de la chaîne qui concorde avec "a*(.)%w(%s*)"
est stockée dans la première capture (et a donc le numéro 1) ; le caractère qui concorde avec ".
" est stocké dans la capture numéro 2, et la partie qui concorde avec "%s*
" a le numéro 3.
La capture vide ()
est spéciale et capture la position courante dans la chaîne (un nombre).
Par exemple si on applique le motif "()aa()"
sur la chaîne "flaaap"
, il y aura deux captures : 3 et 5.
Un motif ne peut contenir de zéros ("\0"
). Utilisez %z
à la place.
La librairie Ustring
modifierEn attendant une rédaction plus détaillée et moins obscure pour les non-programmeurs, des fonctions de cette librairie, cette section est issue d'une recopie d'une partie de la page : mw:Extension:Scribunto/Lua reference manual/fr
La librairie Ustring est une reprise de la librairie string étudiée précédemment, mais pouvant opérer sur des caractères UTF-8.
La plupart des fonctions génèrent une erreur si la chaîne n’est pas valide en codage UTF-8.
mw.ustring.maxPatternLength
modifierDonne la taille maximum autorisée pour un motif (pattern), en octets.
mw.ustring.maxStringLength
modifierDonne la taille maximum autorisée pour une chaîne, en octets.
mw.ustring.byte
modifiermw.ustring.byte( s, i, j )
Retourne les octets d'une chaîne. Identique à string.byte().
mw.ustring.byteoffset
modifiermw.ustring.byteoffset( s, l, i )
Retourne la position en octets d'un caractère dans la chaîne. La valeur par défaut pour l
et i
est 1. i
peut être négatif, auquel cas c’est une position à partir de la fin.
Le caractère à l
== 1 est le premier caractère qui commence à ou à partir de la position i
. Le caractère à l
== 0 est le premier caractère qui commence à ou avant la position i
. Notez que ce peut être le même caractère. Des valeurs plus grandes ou plus petites de l
sont calculées en relatif. (NDT : pas clair. À préciser)
mw.ustring.char
modifiermw.ustring.char( ... )
Similaire à string.char(), mais les entiers retournés sont des points de codes Unicode et non des octets.
mw.ustring.codepoint
modifiermw.ustring.codepoint( s, i, j )
Similaire à string.byte(), mais les valeurs retournées sont des points de code Unicode et les positions sont celles des caractères et non des octets.
mw.ustring.find
modifiermw.ustring.find( s, pattern, init, plain )
Similaire à string.find(), mais le motif (pattern) est géré comme décrit dans motifs ustring et la position init
est en caractères et non en octets.
- Exemple Unicode
print mw.ustring.find("Salut à vous", "à") -- 7 7
Si la chaine recherchée contient des opérateurs regex, ils peuvent être interprétés. Pour s'en prémunir, il peut être utile de les échapper à l’aide de la fonction suivante :
function p._escapePattern( pattern_str )
return mw.ustring.gsub( pattern_str, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" );
end
|
- Exemple avec "?"
print mw.ustring.find("Comment allez-vous ?", "vous ?") -- "15 19"
print mw.ustring.find("Comment allez-vous ?", p._escapePattern("vous ?")) -- "15 20"
mw.ustring.format
modifiermw.ustring.format( format, ... )
Identique à string.format(). Les tailles et précisions pour les chaînes sont en octets et non en points de code Unicode.
mw.ustring.gcodepoint
modifiermw.ustring.gcodepoint( s, i, j )
Retourne trois valeurs pour itérer sur les points de code Unicode de la chaîne. i
vaut 1 par défaut et j
vaut -1 par défaut. Ca sert à être utilisé par la forme itérateur de for
:
for codepoint in mw.ustring.gcodepoint( s ) do block end
mw.ustring.gmatch
modifiermw.ustring.gmatch( s, pattern )
Similaire à string.gmatch(), mais le motif (pattern) est géré comme décrit dans motifs ustring.
mw.ustring.gsub
modifiermw.ustring.gsub( s, pattern, repl[, n] )
Similaire à string.gsub(), mais le motif (pattern) est géré comme décrit dans motifs ustring.
mw.ustring.isutf8
modifiermw.ustring.isutf8( s )
Retourne vrai si la chaîne est en UTF-8 valide, faux sinon.
mw.ustring.len
modifiermw.ustring.len( s )
Retourne la longueur de la chaîne en point de code Unicode ou nil si la chaîne n’est pas en UTF-8 valide.
Voir string.len() pour une fonction similaire qui utilise la longueur des bits plutôt que des points de code.
mw.ustring.lower
modifiermw.ustring.lower( s )
Similaire à string.lower() à part que la fonction suit le format Unicode.
Si la librairie Language est également chargée, ceci utilisera lc() sur la langue courante à la place.
mw.ustring.match
modifiermw.ustring.match( s, pattern, init )
Similaire à string.match(), mais le motif (pattern) est géré comme décrit dans motifs ustring et la position init
est en caractères et non en octets.
mw.ustring.rep
modifiermw.ustring.rep( s, n )
Identique à string.rep().
mw.ustring.sub
modifiermw.ustring.sub( s, i, j )
Similaire à string.sub(), mais les positions sont en caractères et non en octets.
mw.ustring.toNFC
modifiermw.ustring.toNFC( s )
Convertit la chaîne en forme normalisée C. Retourne nil si la chaîne n’est pas valide en UTF-8.
mw.ustring.toNFD
modifiermw.ustring.toNFD( s )
Convertit la chaîne en forme normalisée D. Retourne nil si la chaîne n’est pas valide en UTF-8.
mw.ustring.upper
modifiermw.ustring.upper( s )
Similaire à string.upper() à part que la fonction suit le format Unicode.
Si la librairie Language est également chargée, ceci utilisera uc() sur la langue courante à la place.
Motifs ustring
modifierAppelé aussi pattern, les motifs dans les fonctions ustring
utilisent la même syntaxe que celle des motifs de la librairie String. La différence principale est que les classes de caractères sont redéfinis en termes de propriétés de caractères Unicode :
%a
: représente tous les caractères de la catégorie "Lettre"%c
: représente tous les caractères de la catégorie "Contrôle"%d
: représente tous les caractères de la catégorie "Nombre décimal".%l
: représente tous les caractères de la catégorie "Lettre minuscule".%p
: représente tous les caractères de la catégorie "Ponctuation".%s
: représente tous les caractères de la catégorie "Séparateur", plus tabulation, saut de ligne, retour chariot, tabulation verticale, et saut de page.%u
: représente tous les caractères de la catégorie "Lettre majuscule"%w
: représente tous les caractères de la catégorie "Lettre" ou "Nombre décimal"%x
: ajoute la version complète des caractères hexadécimaux
Dans tous les cas, les caractères sont interprétés comme des caractères Unicode et non des octets, donc des séries comme [0-9]
, des motifs comme %b«»
, et des quantificateurs appliqués à des caractères multi-octets fonctionnent correctement. La capture vide capturera des positions en codes Unicode et non en octets.
La librairie Text
modifierLes exemples de ce chapitre se trouveront dans le Module:Text
Nous commencerons par visualiser le contenu de la librairie Text grâce au programme suivant :
local p = {}
function p.visualisation(frame)
reponse = ""
for index, objet in pairs(mw.text) do
reponse = reponse.."<br />À la clé "..index..", on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Text|visualisation}} nous donne :
À la clé listToText, on trouve un objet de type : function
À la clé gsplit, on trouve un objet de type : function
À la clé nowiki, on trouve un objet de type : function
À la clé encode, on trouve un objet de type : function
À la clé JSON_PRETTY, on trouve un objet de type : number
À la clé decode, on trouve un objet de type : function
À la clé jsonEncode, on trouve un objet de type : function
À la clé truncate, on trouve un objet de type : function
À la clé trim, on trouve un objet de type : function
À la clé killMarkers, on trouve un objet de type : function
À la clé tag, on trouve un objet de type : function
À la clé unstripNoWiki, on trouve un objet de type : function
À la clé split, on trouve un objet de type : function
À la clé JSON_PRESERVE_KEYS, on trouve un objet de type : number
À la clé jsonDecode, on trouve un objet de type : function
À la clé JSON_TRY_FIXING, on trouve un objet de type : number
À la clé unstrip, on trouve un objet de type : function
Nous voyons que nous avons 12 fonctions. Étudions les dans l’ordre où elles sont sorties.
mw.text.listToText
modifiermw.text.listToText( list )
mw.text.listToText( list, separator, conjunction )
Joint une liste sous forme « textuelle ». Fait la même chose que table.concat()
mais avec un séparateur différent pour l'élément final.
Le séparateur par défaut est récupéré dans MediaWiki:comma-separator dans la langue locale, et la conjonction par défaut est le MediaWiki:and concaténé avec le MediaWiki:word-separator.
Exemples :
-- retourne une chaîne vide mw.text.listToText( {} ) -- retourne "1" mw.text.listToText( { 1 } ) -- retourne "1 et 2" mw.text.listToText( { 1, 2 } ) -- retourne "1, 2, 3, 4 et 5" mw.text.listToText( { 1, 2, 3, 4, 5 } ) -- retourne "1; 2; 3; 4 ou 5" mw.text.listToText( { 1, 2, 3, 4, 5 }, '; ', ' ou ' )
mw.text.gsplit
modifiermw.text.gsplit( s, pattern, plain )
Retourne une fonction itérative qui va itérer sur les sous-chaînes qui auraient été retournées par un appel équivalent à mw.text.split()
.
mw.text.truncate
modifiermw.text.truncate( text, length )
mw.text.truncate( text, length, ellipsis )
mw.text.truncate( text, length, ellipsis, adjustLength )
Tronque text
à la longueur indiquée, en ajoutant ellipsis
si une troncature est effectuée. Si length est positif, la fin de la chaîne est tronquée ; s'il est négatif, c’est le début de la chaîne qui est enlevé. Si adjustLength
est présent et vrai, le résultat en incluant ellipsis ne sera pas plus long que la longueur précisée.
La valeur par défaut pour ellipsis
est prise dans MediaWiki:ellipsis pour le wiki local.
Exemples utilisant "..." pour ellipsis :
-- retourne "foobarbaz" mw.text.truncate( "foobarbaz", 9 ) -- retourne "fooba..." mw.text.truncate( "foobarbaz", 5 ) -- retourne "...arbaz" mw.text.truncate( "foobarbaz", -5 ) -- retourne "foo..." mw.text.truncate( "foobarbaz", 6, nil, true ) -- retourne "foobarbaz", car c’est plus court que "foobarba..." mw.text.truncate( "foobarbaz", 8 )
mw.text.encode
modifiermw.text.encode( s )
mw.text.encode( s, charset )
Remplace les caractères de la chaîne par des entités HTML. Les caractères '<', '>', '&', '"', et l'espace insécable sont remplacés par l'entité HTML correspondante. Tous les autres sont remplacés par l'entité HTML numérique correspondante.
Si charset
est indiqué, il doit contenir une chaîne pouvant être utilisée dans une classe de caractères des motifs Ustring, par exemple "set" dans [set]
. Par défaut : '<>&"\' '
(l'espace à la fin est l'espace insécable U+00A0).
mw.text.trim
modifiermw.text.trim( s )
mw.text.trim( s, charset )
Enlève les espaces et autres caractères au début et à la fin d'une chaîne.
Si charset
est indiqué, il doit contenir une chaîne syntaxiquement compatible avec les classes de caractères des motifs Ustring, par exemple "set" dans [set]
. Par défaut : l'espace ASCII, "%t%r%n%f"
.
mw.text.killMarkers
modifiermw.text.killMarkers( s )
Retire d'une chaîne toutes les balises spécifiques à MediaWiki.
mw.text.tag
modifiermw.text.tag{ name = string, attrs = table, content = string|bool }
- Notez l’utilisation de paramètres nommés.
Génère un tag à la façon HTML pour name
.
Si attrs
est indiqué, il doit être une table avec des clés de type chaîne. Les valeurs de ces clés sont utilisées comme valeurs des attributs. Une valeur booléenne true insère un paramètre HTML5 sans valeur, et false ignore le paramètre. Toute autre valeur génère une erreur.
Si content
n’est pas présent (ou vaut nil), seul le tag ouvrant est retourné. Si content
vaut false, un tag auto-fermant est retourné. Sinon il doit être un nombre ou une chaîne auquel cas ce contenu est inséré entre l'ouverture et la fermeture du tag. Notez que le contenu n’est pas automatiquement encodé en HTML ; utiliser mw.text.encode() au besoin.
Pour retourner proprement un tag comme <ref>
, utilisez plutôt frame:extensionTag().
mw.text.decode
modifiermw.text.decode( s )
mw.text.decode( s, decodeNamedEntities )
Remplace les entités HTML de la chaîne par les caractères correspondant.
Si decodeNamedEntities
est absent ou false, les seules entités reconnues sont '<', '>', '&', '"', et ' '. Sinon la liste des entités HTML5 à traiter est chargée depuis la fonction PHP get_html_translation_table
.
mw.text.nowiki
modifiermw.text.nowiki( s )
Remplace divers caractères dans la chaîne par des entités HTML pour éviter leur interprétation comme wikitexte. Ceci comprend :
- Les caractères suivants : '"', '&', "'", '<', '=', '>', '[', ']', '{', '|', '}'
- Les caractères suivants au début de la chaîne ou juste après un retour à la ligne : '#', '*', ':', ';', espace, tabulation ('\t')
- Les lignes blanches auront le caractère de nouvelle ligne (LF) ou de retour chariot (CR) d'échappé
- "----" au début de la chaine ou juste après un saut de ligne verra son premier '-' échappé
- "__" aura un tiret bas d'échappé
- "://" aura le deux-point échappé
- Un espace blanc suivant "ISBN", "RFC", ou "PMID" sera échappé
mw.text.split
modifiermw.text.split( s, pattern, plain )
Découpe une chaîne en sous-chaînes dont les limites correspondent au motif Ustring pattern
. Si plain
est présent et vrai, pattern
est interprété comme une chaîne littérale et non comme un motif Lua (comme le paramètre de même nom pour mw.ustring.find()
). Retourne une table contenant les sous-chaînes.
Par exemple mw.text.split( 'a b\tc\nd', '%s' )
retourne une table { 'a', 'b', 'c', 'd' }
.
Si pattern
correspond à la chaîne vide, s
sera découpé en caractères individuels.
mw.text.unstripNoWiki
modifiermw.text.unstripNoWiki( s )
Renvoie la chaine entre balises <nowiki>. Les autres balises restent inchangées.
mw.text.unstrip
modifiermw.text.unstrip( s )
Équivalent à mw.text.killMarkers( mw.text.unstripNoWiki( s ) )
.
This no longer reveals the HTML behind special page transclusion, <ref> tags, and so on as it did in earlier versions of Scribunto.
Fonctions mathématiques
Nous allons, dans ce chapitre, étudier quelques fonctions préprogrammées dans le domaine des mathématiques. Comme dans le chapitre précédent, nous ne rangerons pas les fonctions dans l’ordre alphabétique, mais nous essaierons de classer les fonctions de la plus simple à la plus compliqué ou de la plus utile à la moins utile.
Tous les exemples de ce chapitre seront rangés dans le Module:Maths.
Fonctions simples
modifierNous avons pas mal de fonctions mathématiques à étudier. Pour simplifier l'étude nous allons étudier, dans ce paragraphe, en même temps, toutes les fonctions mathématiques classiques qui acceptent en entrée un nombre réel quelconque et qui retourne un seul nombre réel. Ce sont les fonctions :
- math.abs ; Retourne la valeur absolue d'un nombre réel.
- math.exp ; Retourne l'exponentielle d'un nombre réel.
- math.cos ; Retourne le cosinus d'un nombre réel.
- math.sin ; Retourne le sinus d'un nombre réel.
- math.cosh ; Retourne le cosinus hyperbolique d'un nombre réel.
- math.sinh ; Retourne le sinus hyperbolique d'un nombre réel.
- math.tanh ; Retourne la tangente hyperbolique d'un nombre réel.
- math.atan ; Retourne l'arc tangente d'un nombre réel.
- math.deg ; Traduit en degré, un nombre donné en radian.
- math.rad ; Traduit en radian, un nombre donné en degré.
- math.ceil ; Retourne le plus petit entier supérieur ou égal à un nombre réel donné.
- math.floor ; Retourne le plus grand entier inférieur ou égal à un nombre réel donné.
Pour tester toutes ces fonctions, nous allons écrire une fonction diverse qui accepte un nombre réel en argument et qui nous retourne l'image de ce nombre réel par toutes les fonctions précédentes :
local p = {}
function p.diverse(frame)
local x = tonumber(frame.args[1])
local reponse = " "
reponse = reponse.."<br>La valeur absolu est "..math.abs(x)
reponse = reponse.."<br>L'exponentielle est "..math.exp(x)
reponse = reponse.."<br>Le cosinus est "..math.cos(x)
reponse = reponse.."<br>Le sinus est "..math.sin(x)
reponse = reponse.."<br>Le cosinus hyperbolique est "..math.cosh(x)
reponse = reponse.."<br>Le sinus hyperbolique est "..math.sinh(x)
reponse = reponse.."<br>La tangente hyperbolique est "..math.tanh(x)
reponse = reponse.."<br>L'arc tangente est "..math.atan(x)
reponse = reponse.."<br>La conversion en degrés est "..math.deg(x)
reponse = reponse.."<br>La conversion en radian est "..math.rad(x)
reponse = reponse.."<br>L'entier immédiatement supérieur est "..math.ceil(x)
reponse = reponse.."<br>L'entier immédiatement inférieur est "..math.floor(x)
return reponse
end
return p
{{#invoke:Maths|diverse|5.2713}} nous indique :
La valeur absolu est 5.2713
L'exponentielle est 194.66886754927
Le cosinus est 0.53026323894546
Le sinus est -0.84783305987857
Le cosinus hyperbolique est 97.337002238748
Le sinus hyperbolique est 97.331865310524
La tangente hyperbolique est 0.99994722532947
L'arc tangente est 1.3833176461628
La conversion en degrés est 302.02324254731
La conversion en radian est 0.092001540860377
L'entier immédiatement supérieur est 6
L'entier immédiatement inférieur est 5
{{#invoke:Maths|diverse|0.13}} nous indique :
La valeur absolu est 0.13
L'exponentielle est 1.1388283833246
Le cosinus est 0.99156189371479
Le sinus est 0.12963414261969
Le cosinus hyperbolique est 1.0084619071226
Le sinus hyperbolique est 0.13036647620203
La tangente hyperbolique est 0.12927258360606
L'arc tangente est 0.12927500404814
La conversion en degrés est 7.4484513367007
La conversion en radian est 0.0022689280275926
L'entier immédiatement supérieur est 1
L'entier immédiatement inférieur est 0
{{#invoke:Maths|diverse|-0.5}} nous indique :
La valeur absolu est 0.5
L'exponentielle est 0.60653065971263
Le cosinus est 0.87758256189037
Le sinus est -0.4794255386042
Le cosinus hyperbolique est 1.1276259652064
Le sinus hyperbolique est -0.52109530549375
La tangente hyperbolique est -0.46211715726001
L'arc tangente est -0.46364760900081
La conversion en degrés est -28.647889756541
La conversion en radian est -0.0087266462599716
L'entier immédiatement supérieur est -0
L'entier immédiatement inférieur est -1
{{#invoke:Maths|diverse|-7.333}} nous indique :
La valeur absolu est 7.333
L'exponentielle est 0.00065360981349759
Le cosinus est 0.49773177909927
Le sinus est -0.8673310072139
Le cosinus hyperbolique est 764.98272100184
Le sinus hyperbolique est -764.98206739203
La tangente hyperbolique est -0.99999914558879
L'arc tangente est -1.4352625273451
La conversion en degrés est -420.14995116943
La conversion en radian est -0.12798499404874
L'entier immédiatement supérieur est -7
L'entier immédiatement inférieur est -8
Fonction simple avec domaine de définition limité
modifierDans ce paragraphe, nous allons étudier les fonctions mathématiques qui ne sont pas définie sur la totalité de l’ensemble des nombres réels et qui nous retourne un seul réel. Ces fonctions sont :
- math.log ; Retourne le logarithme népérien d'un nombre réel.
- math.log10 ; Retourne le logarithme décimal d'un nombre réel.
- math.tan ; Retourne la tangente d'un nombre réel.
- math.acos ; Retourne l'arc cosinus d'un nombre réel.
- math.asin ; Retourne l'arc sinus d'un nombre réel.
- math.sqrt ; Retourne la racine carrée d'un nombre réel.
Toutes ces fonctions ne sont pas définies sur la totalité de l’ensemble des nombres réels. Si on leur donne en argument, un nombre réel qui n'appartient pas à leur domaine de définition, ces fonctions retourneront nan ou -nan. On remarque qu'elle ne retourne pas nil comme on aurait pu le croire à priori.
Écrivons une fonction diverse2 qui, comme le paragraphe précédent permet d’avoir le retour de toutes ces fonctions à un nombre donné :
local p = {}
function p.diverse2(frame)
local x = tonumber(frame.args[1])
local reponse = " "
reponse = reponse.."<br>Le logarithme népérien est "..math.log(x)
reponse = reponse.."<br>Le logarithme décimal est "..math.log10(x)
reponse = reponse.."<br>La tangente est "..math.tan(x)
reponse = reponse.."<br>L'arc cosinus est "..math.acos(x)
reponse = reponse.."<br>L'arc sinus est "..math.asin(x)
reponse = reponse.."<br>La racine carré est "..math.sqrt(x)
return reponse
end
return p
{{#invoke:Maths|diverse2|1.570796326794896}} nous indique :
Le logarithme népérien est 0.45158270528945
Le logarithme décimal est 0.19611987703015
La tangente est 1.3748233863972e+15
L'arc cosinus est nan
L'arc sinus est nan
La racine carré est 1.2533141373155
{{#invoke:Maths|diverse2|0.13}} nous indique :
Le logarithme népérien est -2.0402208285266
Le logarithme décimal est -0.88605664769316
La tangente est 0.13073731800446
L'arc cosinus est 1.4404273470918
L'arc sinus est 0.13036897970315
La racine carré est 0.3605551275464
{{#invoke:Maths|diverse2|-0.5}} nous indique :
Le logarithme népérien est nan
Le logarithme décimal est nan
La tangente est -0.54630248984379
L'arc cosinus est 2.0943951023932
L'arc sinus est -0.5235987755983
La racine carré est -nan
{{#invoke:Maths|diverse2|-7.333}} nous indique :
Le logarithme népérien est nan
Le logarithme décimal est nan
La tangente est -1.7425670685193
L'arc cosinus est nan
L'arc sinus est nan
La racine carré est -nan
La valeur 1.570796326794896 est une valeur approchée de π/2 qui n'appartient pas au domaine de définition de la fonction tangente. Pour cette valeur, nous avons malgré tout obtenu 1.3748233863972e+15 et non pas nan. On peut simuler la constante π à l'aide de la fonction préprogrammée math.pi. Essayons d'écrire une fonction tanpi qui calcule la tangente de π/2 en utilisant math.pi :
local p = {}
function p.tanpi()
return math.tan(math.pi/2)
end
return p
{{#invoke:Maths|tanpi}} nous donne : 1.6331239353195e+16
Nous constatons que nous obtenons une valeur encore plus grande que précédemment, mais nous n'obtenons toujours pas nan. Bien que la fonction math.tan soit à domaine de définition limité, il semblerait qu’il soit impossible de lui donner, en argument, un nombre réel tel qu'elle nous retourne nan.
math.max
modifierCette fonction retourne le plus grand de ses paramètres.
math.min
modifierCette fonction retourne le plus petit de ses paramètres.
math.modf
modifierCette fonction mathématiques permet d'obtenir la partie entière et la partie fractionnaire d'un nombre donné. Elle retourne donc deux valeurs. À titre d'exemple, nous écrirons une fonction p.separation qui nous indiquera, sous forme rédigée, la partie entière et la partie fractionnaire d'un nombre décimal entré en argument :
local p = {}
function p.separation(frame)
local a,b = math.modf(frame.args[1])
return "La partie entière est "..a.." et la partie fractionnaire est "..b
end
return p
{{#invoke:Maths|separation|5.2713}} nous indique : La partie entière est 5 et la partie fractionnaire est 0.2713
math.fmod
modifierCette fonction admet deux paramètres, math.fmod(n,d). Elle retourne le reste de la division de n par d. Une autre façon de voir serait de dire qu'elle nous donne la classe de congruence de n modulo d.
math.random et math.randomseed
modifiermath.random est une fonction qui retourne un nombre pseudo-aléatoire. Si l’on ne met pas de paramètre, ce nombre pseudo aléatoire sera dans l'intervalle [0;1[. Si l’on y met un paramètre n entier, le nombre pseudo-aléatoire sera un entier dans l'intervalle [1;n]. Si l’on y met deux paramètres entiers m et n, le nombre pseudo-aléatoire sera un entier dans l'intervalle [m;n].
Si la fonction est utilisée plusieurs fois dans un programme, elle donnera une liste de nombres pseudos aléatoires qui seront toujours les mêmes à chaque exécution du programme. Si l’on veut une liste de nombres différentes, il faut utiliser la fonction math.randomseed. La fonction math.randomseed admet un paramètre et selon la valeur de ce paramètre, la fonction math.random fournira une liste de nombres aléatoires qui sera différentes.
Seule la partie entière du paramètre passée à la fonction math.randomseed sera utilisée. La fonction math.randomseed ne fera pas la différence, par exemple, entre 47.3 et 47.8. Autant se contenter de lui transmettre un nombre entier ! |
Bien sûr, si l’on fournit toujours le même paramètre à la fonction math.randomseed, on obtiendra toujours la même liste. Si on veut une liste de nombre qui semble vraiment aléatoire et différente à chaque utilisation du programme, il faut trouver un moyen de fournir à la fonction math.randomseed un paramètre différent à chaque utilisation du programme. La première méthode est que l'utilisateur du programme choisisse lui-même un paramètre différent à chaque exécution du programme. Ici, cela risque d’être difficile à réaliser compte tenu que l’on ne peut pas rentrer un paramètre en temps réel à chaque utilisation du programme.
La seconde méthode consiste à utiliser une fonction qui fasse appel à une donnée extérieure au programme et qui soit susceptible de varier d'une exécution à l'autre comme par exemple l’heure à laquelle démarre le programme. Nous approfondirons cette idée en exercice.
Pour le moment, écrivons, dans le Module:maths, une fonction p.alea qui teste ce que l’on vient de dire. C'est-à-dire qui utilise la fonction math.randomseed, auquel on aura transmit un paramètre grâce à #invoke, suivi de la fonction math.random qui fournira une vingtaine de nombres aléatoires grâce à une boucle for..do. Pour être complet, nous pourrons aussi transmettre au programme les deux paramètres m et n décrit dans la présentation de la fonction math.random.
local p = {}
function p.alea(frame)
math.randomseed(frame.args[1])
local m,n = frame.args[2],frame.args[3]
local reponse = ""
for x = 1, 20 do
if m ~= nil and n ~= nil then
reponse = reponse..math.random(m,n).." "
elseif m ~= nil then
reponse = reponse..math.random(m).." "
else
reponse = reponse..math.random().." "
end
end
return reponse
end
return p
{{#invoke:Maths|alea|3|5|15}} nous donne : 12 5 5 13 13 9 15 8 8 11 7 7 7 5 15 9 14 12 6 15
Autres fonctions standards
Dans les chapitres précédents, nous avons étudié certaines catégories de fonctions. Ces catégories étaient suffisamment grandes pour que nous leur consacrions un chapitre entier. Il nous reste toutefois à étudier d'autres catégories de fonctions qui contiennent peu de fonctions. Nous consacrons donc ici ce chapitre aux fonctions qui appartiennent aux catégories restantes.
Fonctions concernant les tables
modifierLes exemples donnés dans ce paragraphe se trouvent dans le Module:Tables.
table.insert
modifierLa fonction table.insert permet de rajouter un élément dans une table avec clé numérique, mais pas forcément à la fin comme on a l'habitude de le faire. La fonction insert permet d'insérer un élément à une clé déjà existante en décalant tous les objets déjà présents à partir de cette clé. Prenons un exemple pour mieux comprendre.
Des voitures s’apprêtent à faire une course sur un circuit. Supposons les voitures numérotées dans lesquelles se trouvent des conducteurs. Supposons que Natacha arrive en retard et veuille absolument conduire la voiture 3 (déjà occupé) car 3 est son numéro fétiche. On va donc donner la voiture 3 à Natacha. Le conducteur de la 3 ira dans la 4. Le conducteur de la 4 ira dans la 5 et ainsi de suite. Ceci est réalisé grâce à l'instruction table.insert(circuit,3,"Natacha")
Le programme décrivant l'occupation des voitures avant et après l'arrivée de Natacha est le suivant :
local p = {}
local circuit = {"Laurent","Cécile","Alain","Cloé","Amandine"}
function p.insertion()
local reponse = " "
reponse = reponse.."<br><u>voitures avant l'arrivée de Natacha.</u>"
for voiture, conducteur in ipairs(circuit) do
reponse = reponse.."<br>La voiture "..voiture.." est conduite par "..conducteur.."."
end
table.insert(circuit,3,"Natacha")
reponse = reponse.."<br><u>voitures après l'arrivée de Natacha dans la voiture 3.</u>"
for voiture, conducteur in ipairs(circuit) do
reponse = reponse.."<br>La voiture "..voiture.." est conduite par "..conducteur.."."
end
return reponse
end
return p
En tapant {{#invoke:Tables|insertion}}, on obtient :
voitures avant l'arrivée de Natacha.
La voiture 1 est conduite par Laurent.
La voiture 2 est conduite par Cécile.
La voiture 3 est conduite par Alain.
La voiture 4 est conduite par Cloé.
La voiture 5 est conduite par Amandine.
voitures après l'arrivée de Natacha dans la voiture 3.
La voiture 1 est conduite par Laurent.
La voiture 2 est conduite par Cécile.
La voiture 3 est conduite par Natacha.
La voiture 4 est conduite par Alain.
La voiture 5 est conduite par Cloé.
La voiture 6 est conduite par Amandine.
table.remove
modifierLa fonction table.remove réalise le contraire de ce que faisait la fonction table.insert. Elle retire un élément de la table avec clé numérique et décale les éléments suivants de façon à boucher le trou.
Reprenons l'exemple précédent en supposant, cette fois qu'Alain, le conducteur de la voiture 3, ne se sente pas bien et renonce à prendre le départ. Pour éviter qu’il y ait une discontinuité dans la numérotation des voitures, le conducteur de la voiture 4 va aller dans la 3. Le conducteur de la 5 va aller dans la 4 et ainsi de suite. Ceci est réalisé grâce à l'instruction table.remove(circuit,3). Cette fonction retourne l'élément retiré.
Le programme décrivant l'occupation des voitures avant et après le départ d'Alain est le suivant :
local p = {}
local circuit = {"Laurent","Cécile","Alain","Cloé","Amandine"}
function p.retrait()
local reponse = " "
local souffrant
reponse = reponse.."<br><u>voitures au départ du circuit.</u>"
for voiture, conducteur in ipairs(circuit) do
reponse = reponse.."<br>La voiture "..voiture.." est conduite par "..conducteur.."."
end
souffrant = table.remove(circuit,3)
reponse = reponse.."<br><u>voitures après le départ de "..souffrant.." de la voiture 3.</u>"
for voiture, conducteur in ipairs(circuit) do
reponse = reponse.."<br>La voiture "..voiture.." est conduite par "..conducteur.."."
end
return reponse
end
return p
En tapant {{#invoke:Tables|retrait}}, on obtient :
voitures au départ du circuit.
La voiture 1 est conduite par Laurent.
La voiture 2 est conduite par Cécile.
La voiture 3 est conduite par Alain.
La voiture 4 est conduite par Cloé.
La voiture 5 est conduite par Amandine.
voitures après le départ de Alain de la voiture 3.
La voiture 1 est conduite par Laurent.
La voiture 2 est conduite par Cécile.
La voiture 3 est conduite par Cloé.
La voiture 4 est conduite par Amandine.
table.concat
modifierLa fonction table.concat permet d'obtenir une chaîne de caractères exposant le contenu (ou une partie du contenu) d'une table. Elle s'utilise sous la forme : table.concat(table, séparateur, début, fin)
Les paramètres séparateur, début et fin sont facultatifs. Si le paramètre séparateur est absent, il n'y aura pas de séparateurs. Si le paramètre début est absent, la concaténation se fera à partir du début de la table. Si le paramètre fin est absent, la concaténation se fera jusqu'à la fin de la table.
Écrivons, en exemple, une fonction qui nous donne les conducteurs des exemples précédents sauf le premier.
local p = {}
local circuit = {"Laurent","Cécile","Alain","Cloé","Amandine"}
function p.conducteurs()
return table.concat( circuit, " et aussi ",2,5)
end
return p
En tapant {{#invoke:Tables|conducteurs}}, on obtient : Cécile et aussi Alain et aussi Cloé et aussi Amandine
table.maxn
modifierRetourne le plus grand index numérique positif utilisé dans la table.
local p = {}
local circuit = {"Laurent","Cécile","Alain","Cloé","Amandine"}
function p.sup()
return table.maxn(circuit)
end
return p
En tapant {{#invoke:Tables|sup}}, on obtient : 5
table.sort
modifierPermet de trier la table.
local p = {}
local circuit = {"Laurent","Cécile","Alain","Cloé","Amandine"}
function p.trie()
table.sort(circuit)
return "le nouvel ordre est : "..table.concat(circuit,", ")
end
return p
En tapant {{#invoke:Tables|trie}}, on obtient : le nouvel ordre est : Alain, Amandine, Cloé, Cécile, Laurent
Nous voyons que les éléments de la table ont été triés par ordre alphabétique.
Fonctions concernant le système d'exploitation
modifier
Les exemples donnés dans ce paragraphe se trouvent dans le Module:Temps.
os.time
modifiercette fonction nous ramène un nombre mystérieux à 10 chiffres qui probablement contient, sous forme codé la date et l’heure.
local p = {}
function p.heure()
return "La fonction os.time nous retourne : "..os.time()
end
return p
{{#invoke:Temps|heure}} nous retourne : La fonction os.time nous retourne : 1734829856
os.difftime
modifierCette fonction semble être destinée à faire la différence entre deux temps donnée sous un format particulier. En tout cas, elle fait la différence entre deux nombres.
local p = {}
function p.delais()
return "La fonction os.difftime nous retourne : "..os.difftime(74,3)
end
return p
{{#invoke:Temps|delais}} nous retourne : La fonction os.difftime nous retourne : 71
os.clock
modifierCette fonction donne le temps CPU d'exécution d'un programme en seconde. Cette fonction permet donc de mesurer le temps qui s'écoule entre deux parties d'un programme. Pour des exemples d'application voir, dans le chapitre sur la gestion de l'environnement, le paragraphe sur la vitesse d'exécution d'un programme. Voir aussi l'exercice 7-1.
Le temps CPU démarre au moment où commence à s'afficher la page dans laquelle se trouve le module contenant la fonction os.clock et pas au moment où est lancé le module. Par conséquent, si dans une page, on met plusieurs modules utilisant la fonction os.clock, on observera un temps d'autant plus grand que le module est près du bas de la page.
Le temps CPU est donné en seconde et est le plus souvent compris entre 2 et 20 millisecondes.
Si l’on veut chronométrer le temps d'exécution d'une partie d'un programme, on est obligé de mettre l'instruction avant et après la partie à chronométrer et de faire la différence entre les deux temps obtenus. On obtient alors un temps généralement très inférieur à la milliseconde.
Compte tenu du fait que nos programmes s'exécutent sur un système multitâche, le temps donné par la fonction os.clock est très approximatif et sera différent d'une exécution à l'autre. |
Si dessous nous présentons un exemple donnant le temps écoulé, depuis le début de l’affichage de cette page, en millisecondes :
local p = {}
function p.horloge()
local reponse = ""
local temps = os.clock()
reponse = reponse.. "<br>La fonction os.clock nous retourne : "..temps
temps = temps*1000
reponse = reponse.."<br>Ce qui signifie que "..temps.." millisecondes se sont écoulées depuis le début de l’affichage de cette page."
return reponse
end
return p
{{#invoke:Temps|horloge}} nous donne :
La fonction os.clock nous retourne : 0.39234
Ce qui signifie que 392.34 millisecondes se sont écoulées depuis le début de l’affichage de cette page.
os.date
modifierCette fonction permet de donner la date et l’heure sous un format choisi. Elle peut fournir une chaîne de caractère ou remplir un tableau avec les différents éléments de la date et de l’heure.
local p = {}
function p.date()
return "La fonction os.date nous retourne : "..os.date()
end
return p
{{#invoke:Temps|date}} nous retourne : La fonction os.date nous retourne : Sun Dec 22 01:10:56 2024
Fonctions concernant la librairie Scribunto
modifier
Les exemples donnés dans ce paragraphe se trouvent dans le Module:Scribunto
mw.clone
modifierNous savons que les tables et les fonctions sont affectées par référence. Si A est une table, l'instruction B = A affectera à B l'adresse de la table A et toutes les modifications effectuées sur A sembleront se répercuter sur B. Pour réaliser une véritable recopie du contenu de la table A dans la table B, on utilisera la fonction préprogrammée mw.clone en écrivant B = mw.clone(A). Cette fois, les tables A et B seront totalement indépendantes bien que contenant les mêmes objets juste après l'affectation.
Dans l'exemple ci-dessous, nous vérifions ce que nous venons de dire en créant une table A que nous affectons à une table B grâce à B = A et à une table C grâce à C = mw.clone(A). Nous modifions ensuite A et nous vérifions que cette modification s'est bien répercutée sur B mais pas sur C.
local p = {}
function p.duplique()
local A = {"truc", "machin", "chose"}
local B = A
local C = mw.clone(A)
A[2] = "bidule"
return "B[2] contient "..B[2].." et C[2] contient "..C[2]
end
return p
{{#invoke:Scribunto|duplique}} nous donne : B[2] contient bidule et C[2] contient machin
mw.loadData
modifierCette fonction permet de charger une table (ou plusieurs) se trouvant dans un autre module. Cette table contient en général un grand nombre de données. Plusieurs raisons justifient le fait que cette table soit écrite dans un autre module :
- Elle est susceptible d’être utilisée par plusieurs modules.
- Cette table n'est chargée qu'une seule fois par page, même si celle-ci utilise plusieurs commandes #invoke, ce qui entraîne une économie de temps.
Certaines restrictions doivent être respectées :
- On ne peut pas charger autre chose qu'une table.
- La table que l’on charge ne doit pas contenir de fonctions.
- Les fonctions pairs et ipairs fonctionnent sur la table retournée. Le fonctionnement des autres fonctions spécialisées dans les tables n’est pas garanti.
Dans l'exemple ci-dessous, la fonction codehex permet de convertir un nom de couleur (de la page Liste de couleurs) en son code hexadécimal qu'elle retourne.
La fonction mw.loadData de l'exemple ci-dessous charge une table se trouvant dans le Module:Nomcouleur. Dans le Module:Nomcouleur, la table chargée se nomme nuancier. On remarquera, en fin de module, l'instruction return nuancier pour que la table puisse être chargée par la fonction mw.loadData à partir d'un autre module.
local p = {}
function p.codehex(frame)
local code = mw.loadData("Module:Nomcouleur")
local couleur = frame.args[1]
return "Le code hexadécimal correspondant à la couleur "..couleur.." est "..code[couleur]
end
return p
Nous voyons, dans l'exemple ci-dessus, que la fonction mw.loadData attend un argument sous forme de chaîne de caractères. Nous lui avons donc donné le nom du module contenant la table à charger, à savoir "Module:Nomcouleur", sans oublier les guillemets et le mot module.
{{#invoke:Scribunto|codehex|caca d'oie}} nous retourne : Le code hexadécimal correspondant à la couleur caca d'oie est cdcd0d
Nota : Si l’on souhaite charger plusieurs tables s'appelant, par exemple, A, B, C à l'aide de la fonction mw.loadData, il faut que dans le module appelé, il y ait l'instruction return {A, B, C}. En fait, on retourne une table de tables.
mw.allToString
modifierLa fonction mw.allTostring convertit en chaînes de caractères tous ses arguments et concatène les chaînes de caractères obtenues en les séparant par une tabulation. Dans l'exemple, ci-dessous, nous convertissons et concaténons des arguments de types différents : Le nombre 2, la chaîne "mouche", le type nil et le booléen true.
local p = {}
function p.converti()
return mw.allToString(2,"Mouche",nil,true)
end
return p
{{#invoke:Scribunto|converti}} nous retourne : 2 Mouche nil true
Nous vérifierons en exercice qu’il y a bien une tabulation comme séparateur.
mw.getCurrentFrame
modifierCette fonction retourne l’objet frame courant. Elle permet donc, par exemple, une recopie de l’objet frame actuel. Dans l'exemple qui suit, l’objet frame a été recopié dans l’objet frami qui peut s'utiliser comme l’objet frame. Un des avantages est que l’utilisation du nouvel objet sera plus rapide car ne nécessitant plus un appel extérieur au programme.
En réalité, l'explication que nous venons de donner est très fragmentaire car nécessitant des éléments que nous n'avons pas encore étudiés. Nous reviendrons donc sur l'étude de cette fonction dans le chapitre consacré à l’objet frame pour essayer d’en donner une explication plus complète.
Pour le moment, nous nous contenterons de l'exemple suivant ou l’objet frame est recopié dans l’objet frami. On visualise ensuite le contenu de l’objet frami comme on le ferait pour l’objet frame.
local p = {}
function p.courant(frame)
frami = mw.getCurrentFrame()
reponse = ""
for i = 1,5 do
reponse = reponse.."<br />à la clé "..i..", on trouve : "..frami.args[i]
end
return reponse
end
return p
{{#invoke:Scribunto|courant|Marmite|17|Tuile|5|Maison}} nous retourne :
à la clé 1, on trouve : Marmite
à la clé 2, on trouve : 17
à la clé 3, on trouve : Tuile
à la clé 4, on trouve : 5
à la clé 5, on trouve : Maison
mw.incrementExpensiveFunctionCount
modifier
mw.log
modifier
Fonctions concernant le chargement (Librairie Package)
modifier
Les exemples donnés dans ce paragraphe se trouvent dans le Module:Package
require
modifierCette fonction sera étudiée dans le chapitre sur la gestion de l'environnement.
package.loaders
modifier
package.preload
modifier
package.seeall
modifier
Gestion de l'environnement
Dans ce chapitre, nous allons étudier quelques aspects plus liés avec l'utilisateur et l'intégration du Lua dans un des projets wikimédia, c'est-à-dire Scribunto. Nous commencerons par la gestion des erreurs. Nous verrons ensuite quelques commandes utiles permettant d’utiliser un module dans le but de réaliser une certaine fonction.
Gestion des erreurs
modifierDans ce paragraphe, nous allons dire quelques mots sur la gestion des erreurs.
Exposé du problème
modifierLorsqu'on fait un programme pour notre usage personnel, on sait généralement ce qu’il contient et on peut alors l’utiliser correctement. Si ce programme est destiné à être utilisé par quelqu’un d'autre, alors on n’est pas sûr que l'autre utilisateur aura bien compris comment marche le programme. L'utilisateur va, peut-être, faire des erreurs en l'utilisant. Le bon programmeur doit être capable d'anticiper toutes les erreurs qu'un utilisateur peut faire et doit prévoir, dans son programme, des instructions pour permettre au programme de réagir correctement en cas d'erreur d'utilisation.
Prenons un exemple :
Reprenons la fonction p.alerte4 que nous avons écrite au troisième chapitre
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 !"
else
reponse = "Grosse vache !!"
end
end
return reponse
end
return p
Nous avons testé cette fonction en écrivant :
{{#invoke:Balance|alerte4|56}}, et nous avons obtenu : Attention, vous commencez à grossir !
Supposons maintenant que l'utilisatrice, pour qui nous avons écrit cette fonction, n'a pas bien compris comment marche cette fonction et écrive le nombre 56 en toute lettre :
{{#invoke:Balance|alerte4|cinquante-six}}, elle obtiendra alors : Erreur Lua dans Module:Balance à la ligne 35 : attempt to compare nil with number.
Que c'est-il passé. En fait, rien de bien compliqué, nous avons vu, dans le chapitre exposant les fonctions préprogrammées de base, que la fonction tonumber ne comprenait pas le français et était incapable de convertir la chaîne de caractère "cinquante-six" en nombre 56. Et dans ce cas là, elle nous retourne nil. par conséquent la variable poids contenait nil au moment où elle a été comparée au nombre 55, ce qui a provoqué l'erreur de script.
Gestion programmée de l'erreur
modifierComment remédier à ce problème ? Il nous suffit simplement d’éviter que la variable poids soit comparée à un nombre lorsqu'elle contient nil et que dans ce cas la fonction nous retourne un message d'erreur informant l'utilisatrice de l'erreur qu'elle a commise. Nous devons donc encore perfectionner notre programme en écrivant une nouvelle fonction p.alerte5 qui met en œuvre ce que l’on vient de dire :
local p = {}
function p.alerte5(frame)
local poids = tonumber(frame.args[1])
local reponse
if poids == nil then
reponse = "Vous n'avez pas rentré un nombre sous un format reconnaissable !"
else
if poids < 55 then
reponse = "Votre poids est acceptable !"
else
if poids < 60 then
reponse = "Attention, vous commencez à grossir !"
else
reponse = "Grosse vache !!"
end
end
end
return reponse
end
return p
Testons cette nouvelle fonction :
En écrivant :
{{#invoke:Balance|alerte5|56}}, nous obtenons : Attention, vous commencez à grossir !
Et en écrivant :
{{#invoke:Balance|alerte5|cinquante-six}}, nous obtenons : Vous n'avez pas rentré un nombre sous un format reconnaissable !
Nous voyons que nous obtenons une réponse appropriée dans tous les cas de figure.
Gestion à l'aide de pcall
modifierUne autre façon de gérer les erreurs pouvant se produire à l'appel d'une fonction est d’utiliser la fonction pcall dont le rôle est justement de gérer les erreurs à l'appel d'une fonction. Il suffit d'invoquer la fonction en lui donnant comme paramètres : le nom de la fonction, ses paramètres et le message d'erreur s'il y a problème.
Pour tester cette fonction écrivons, dans le Module:Balance une nouvelle fonction p.alerte6 ainsi :
local p = {}
function p.alerte6(frame)
local poids = pcall(tonumber,frame.args[1],"Vous n'avez pas rentré un nombre sous un format reconnaissable !")
local reponse
if poids < 55 then
reponse = "Votre poids est acceptable"
else
if poids < 60 then
reponse = "Attention, vous commencez à grossir !"
else
reponse = "Grosse vache !!"
end
end
return reponse
end
return p
Testons pour voir si ça marche :
En écrivant :
{{#invoke:Balance|alerte6|56}}, nous obtenons : Attention, vous commencez à grossir !
Et en écrivant :
{{#invoke:Balance|alerte6|cinquante-six}}, nous obtenons : Vous n'avez pas rentré un nombre sous un format reconnaissable !
Rapidité d'exécution d'un programme
modifierEn informatique, un programme, bien qu’il s'exécute correctement, peut ne pas donner entièrement satisfaction si son exécution ne se fait pas assez rapidement. Nous n'avons pas évoqué le problème jusqu'à maintenant, mais le programmeur doit garder présent à l'esprit que son programme doit s'exécuter le plus rapidement possible pour éviter de trop monopoliser la machine qui exécute son programme. Bien souvent, pour exécuter une tache particulière, plusieurs solutions s'offrent à nous. Nous devons alors choisir, parmi toutes les solutions possibles, celle dont le temps d’exécution sera le plus bref possible. Prenons un exemple :
Supposons que nous voulions écrire une fonction qui nous renvoie la valeur du polynôme p(x) = 7x4+5x3+3x2+x+2. Une première façon d'écrire cette fonction pourrait être :
local p = {}
function p.poly(frame)
return 7*frame.args[1]^4+5*frame.args[1]^3+3*frame.args[1]^2+frame.args[1]+2
end
return p
Cette façon d'écrire le programme, bien que fonctionnant parfaitement, n’est pas correcte. Pourquoi ?
En fait, lorsqu'on écrit frame.args[1], on fait appel à la valeur de x que nous a fourni l'utilisateur dans la commande #invoke et qui est donc quelque chose d'extérieur au programme. Allez chercher cette valeur met en œuvre des routines qui sont, à elles seules, des programmes qu’il faut appeler et exécuter. tout cela demande du temps. On comprend donc aisément qu'écrire quatre fois frame.args[1] va demander un temps d'exécution plus long que si l’on ne l'écrivait qu'une seule fois. Il est donc bien préférable d'écrire la fonction en s'arrangeant pour n'avoir qu'une seule exécution de frame.args[1]. À la première façon d'écrire le programme, nous préférerons donc la deuxième façon suivante :
local p = {}
function p.poly(frame)
local x = frame.args[1]
return 7*x^4+5*x^3+3*x^2+x+2
end
return p
On pourrait, à ce niveau, être satisfait, de notre programme et penser que l’on a la meilleure façon possible de l'écrire. En fait, il n'en est rien ! Il est encore possible d'améliorer le temps d'exécution de la fonction p.poly. Cette façon d'écrire un polynôme, en informatique, n’est pas correcte. Si nous comptons les multiplications et les additions, nous voyons qu’il y a 9 multiplications et 4 additions. Peut-on imaginer une façon d'écrire un polynôme de façon à réduire le nombre d'opérations. Cela est possible en remarquant, tout simplement, que :
Si nous comptons le nombre de multiplication et d'addition dans l’expression x(x(x(7x+5)+3)+1)+2, nous voyons qu’il y a 4 multiplications et 4 additions. Nous avons 5 multiplications de moins à faire dans la calcul de x(x(x(7x+5)+3)+1)+2 que dans la calcul de 7x4+5x3+3x2+x+2, d'où le gain de temps. Nous pouvons donc améliorer encore le temps d'exécution de la fonction p.poly en l'écrivant :
local p = {}
function p.poly(frame)
local x = frame.args[1]
return (((7*x+5)*x+3)*x+2)*x+1
end
return p
Nous pouvons ainsi espérer avoir la meilleure façon possible d'écrire notre fonction !
La question qui peut maintenant venir à l'esprit est la suivante : Supposons que l’on ait plusieurs façons d'écrire un programme, comment peut-on savoir laquelle de ces façons est la plus rapide ?
Pour cela, nous disposons d'une fonction préprogrammée qui se nomme os.clock et qui est capable de nous donner une approximation du temps d'exécution du programme que l’on est en train d'écrire. Nous allons donc, dans un Module:Polynôme essayer de comparer les temps d'exécution des trois façons d'écrire le programme de calcul de la fonction p.poly vue précédemment. En fait, nous allons écrire trois fonctions : p.poly1, p.poly2 et p.poly3 qui, en plus du résultat nous renvoie le temps d'exécution.
local p = {}
function p.poly1(frame)
local temps = os.clock()
local reponse = "Le résultat est "
reponse = reponse..7*frame.args[1]^4+5*frame.args[1]^3+3*frame.args[1]^2+frame.args[1]+2
temps = os.clock() - temps
reponse = reponse.." et le temps d'exécution est "..temps
return reponse
end
function p.poly2(frame)
local temps = os.clock()
local x = frame.args[1]
local reponse = "Le résultat est "
reponse = reponse..7*x^4+5*x^3+3*x^2+x+2
temps = os.clock() - temps
reponse = reponse.." et le temps d'exécution est "..temps
return reponse
end
function p.poly3(frame)
local temps = os.clock()
local x = frame.args[1]
local reponse = "Le résultat est "
reponse = reponse..(((7*x+5)*x+3)*x+1)*x+2
temps = os.clock() - temps
reponse = reponse.." et le temps d'exécution est "..temps
return reponse
end
return p
Nous remarquons que, pour chaque fonction, nous prenons le temps en début de fonction et en fin de fonction et nous faisons la différence des deux temps pour avoir une estimation du temps d'exécution de la fonction seule. Si nous ne faisions pas cela, nous aurions, à la fin de la troisième fonction, le temps cumulé des trois fonctions car le compteur temps ne s’arrête pas d'une fonction à l'autre.
{{#invoke:Polynôme|poly1|7}} nous donne : Le résultat est 18678 et le temps d'exécution est 0.00012000000000001
{{#invoke:Polynôme|poly2|7}} nous donne : Le résultat est 18678 et le temps d'exécution est 4.000000000004e-05
{{#invoke:Polynôme|poly3|7}} nous donne : Le résultat est 18678 et le temps d'exécution est 1.9999999999964e-05
Nous voyons maintenant clairement que la meilleure des trois fonctions est bien la troisième et la plus mauvaise est bien la première.
En fait, le temps d'exécution obtenu par cette méthode est très approximatif, car nous fonctionnons sur un système multitâche et l'exécution d'une fonction peut être perturbée selon la charge du processeur. Dans l'exécution ci-dessus, nous pouvons même avoir quelquefois un temps d'exécution, de la troisième fonction, supérieur à la seconde. Pour mieux se rendre compte de la rapidité d'exécution de chaque fonction, il est donc conseillé de purger le cache de la page plusieurs fois pour relancer plusieurs fois l'exécution des fonctions et ainsi mieux se rendre compte. |
Comment utiliser des fonctions écrites dans un autre module
modifierDans les chapitres précédents, nous avons dit qu'une variable ou une fonction déclarée avec le mot-clé local n'est utilisable qu’à l'intérieur du module où elles sont déclarées. Cette affirmation sous-entend que si l’on n'emploie pas le mot-clé local, alors la variable ou la fonction déclarées devrait pouvoir être utilisée dans un autre module. Dans ce paragraphe, nous allons donc étudier comment utiliser les variables et les fonctions créées dans un autre module sans le mot-clé local.
Le principal intérêt de cette possibilité va être de pouvoir se confectionner des modules contenant des fonctions qui peuvent être utiles dans plusieurs autres modules en évitant ainsi de devoir les réécrire dans chaque module.
La fonction préprogrammée qui va nous permettre d'appeler le contenu d'un autre module est la fonction require. Il y a deux façons d’utiliser la fonction require selon que l’on souhaite récupérer des objets, dans un autre module, qui ne se trouve pas dans la table que l’on a pris l'habitude d'appeler p (mais qui pourrait s'appeler autrement) ou que l’on souhaite récupérer des objets qui sont dans une table p.
Première façon : On souhaite récupérer des objets indépendants de la table p
modifierIl est, bien sûr, indispensable que les objets que l’on souhaite récupérer ne soient pas déclarés en local.
Prenons un exemple : Dans le Module:Fonction, nous avions écrit une fonction f qui élève un nombre au carré. Dans un autre Module:Aspire, essayons d'y inclure le module Fonction et de créer une fonction carre qui appelle la fonction f pour élever un nombre au carré :
local p = {}
require("Module:Fonction")
function p.carre(frame)
return f(frame.args[1])
end
return p
La fonction require attend une chaîne de caractères. Nous avons donc dû mettre Module:Fonction entre guillemet. Ne pas oublier, aussi, le mot Module. Si l’on avait écrit require("Fonction"), nous aurions eu une erreur de script.
{{#invoke:Aspire|carre|7}} nous donne : 49
Nous avons bien obtenu 7 au carré qui donne 49. On peut aussi vérifier que l’on peut récupérer une variable déclarée dans le module que l’on appelle avec require a condition que cette variable ne soit pas déclarée avec le mot clé local, sinon on obtient une erreur de script.
Deuxième façon : L'objet que l’on souhaite récupérer est dans une table p
modifierC'est un peu comme si l’on souhaitait utiliser la commande #invoke à partir d'un autre module. Mais cette commande ne marche pas si elle est utilisée dans un module. Nous allons donc transférer le contenu de la table p dans une table locale au module appelant.
Prenons un exemple : Dans un Module:Ingère écrivons une fonction compo qui appelle la fonction p.cube se trouvant dans le Module:Réservoir
Le contenu du module Réservoir est :
local p = {}
function p.cube(nombre)
return nombre^3
end
return p
La grosse différence avec ce que l’on avait l'habitude de voir est que l’on ne trouve pas frame entre les parenthèses de la fonction p.cube puisque, cette fois, nous n'avons pas l'intention d’utiliser la commande #invoke. À part cela, le reste est identique !
Question : Peut-on appeler, à partir d'un autre module, une fonction qui aurait frame entre parenthèse ? Réponse : Non, car frame implique obligatoirement l’utilisation de la commande #invoke. Et cette commande ne peut pas être utilisée dans un module. |
Le contenu du module Ingère est :
local p = {}
local t = require("Module:Réservoir")
function p.compo(frame)
local a = frame.args[1]
return t.cube(a)
end
return p
Le contenu de la table p du module Réservoir est transféré dans la table t du module Ingère. La fonction p.cube est devenue la fonction t.cube.
{{#invoke:Ingère|compo|3}} nous donne alors : 27
Priorité de l'interpréteur
modifierEn général, l'endroit le plus adéquat pour appeler un module est de le faire à partir d'un modèle. Ceci est fortement conseillé pour éviter de surcharger l'espace principal avec la commande #invoke. Par conséquent, le plus souvent, les modèles appelleront les modules. Quelquefois, on risque de devoir faire le contraire. C'est-à-dire d'appeler un modèle dans un module. Que se passe-t-il alors ? Nous allons tester cette opération en prenant un exemple. Essayons d'écrire un module qui aurait pour fonction d'encadrer un texte en faisant appel au Modèle:Encadre. Dans un Module:Cadre, nous serions tenté d'écrire une fonction p.cadre1 ainsi :
local p = {}
function p.cadre1(frame)
return "{{Encadre|contenu="..frame.args[1].."}}"
end
return p
{{#invoke:Cadre|cadre1|Coucou, je suis dans un cadre!}} nous donne alors : {{Encadre|contenu=Coucou, je suis dans un cadre!}}
Et là, avec un grand désarroi, nous constatons que cela ne marche pas. Que s'est-il passé ? En fait, l'interpréteur de mediawiki évalue les modèles avant d’avoir les retours des modules. Et, par conséquent, quand le module Cadre nous ramène {{Encadre|contenu=Coucou, je suis dans un cadre!}}, il est déjà trop tard !
Heureusement, la situation n’est pas désespérée car nous disposons, dans notre lua avec scribunto, d'une fonction préprogrammée frame:preprocess qui va évaluer les modèles avant que ceux-ci ne soient retournés. Pour expérimenter cela, nous allons donc écrire, dans le Module:Cadre, une nouvelle fonction p.cadre2, ainsi :
local p = {}
function p.cadre2(frame)
return frame:preprocess("{{Encadre|contenu="..frame.args[1].."}}")
end
return p
{{#invoke:Cadre|cadre2|Coucou, je suis dans un cadre!}} nous donne enfin :
Coucou, je suis dans un cadre! |
Et là, ça marche, youpi !
Méta-tables
Dans ce chapitre, nous allons étudier les méta-tables. La tradition voudrait que l’on explique les méta-tables de façon hermétique pour que personne n'y comprenne rien (voir, par exemple, mw:Extension:Scribunto/Lua reference manual/fr, paragraphe sur les méta-tables). Nous allons essayer toutefois, dans ce chapitre, de déroger à la tradition, quitte à s'attirer les foudres de ceux qui voudraient que cette partie reste réservée à de rares initiés.
Position du problème
modifierSi l’on se pose la question : "Quels sont les objets qui sont le plus susceptibles de provoquer une erreur de script ?", la réponse serait, tout naturellement "les tables". En effet, si l’on nous demande de provoquer une erreur de script, en utilisant des tables, nous n'avons que l'embarras du choix. On peut y arriver en utilisant une clé inexistante dans la table et concaténer le retour (nil) avec une chaîne de caractères. On peut essayer d'additionner deux tables t1 et t2 (t1 + t2). On peut essayer de les multiplier, les soustraire, les diviser, les concaténer, etc. Bref, aucune opération n'est possible avec les tables ! Pourtant, si l’on programme dans un domaine particulier comme, par exemple, les mathématiques, on aimerait bien pouvoir effectuer certaines opérations directement sur les tables sans provoquer d'erreur de script.
Prenons un exemple : En mathématique, on peut représenter les coordonnées d'un point, dans un repère, par une matrice colonne et si l’on souhaite faire un programme réalisant certains calculs sur les points dans l'espace, on va naturellement ranger les coordonnées d'un point dans une table avec clé numérique ou pas.
Si le point B a pour coordonnées (3,7) et le point C a pour coordonnée (1,8), on écrira
avec clé numérique:
local B = {3,7}
local C = {1,8}
avec clé sous forme de chaîne de caractères :
local B = {["abscisse"] = 3, ["ordonnée"] = 7}
local C = {["abscisse"] = 1, ["ordonnée"] = 8}
Si, dans notre programme, on souhaite calculer le milieu I du segment [BC], on aimerait bien écrire :
Mais ce n’est pas possible car B + C provoque une erreur de script.
Les concepteurs du Lua ont donc eu l’idée de rendre possible les opérations sur les tables qui provoquait, par exemple, une erreur de script. Mais comme une même opération peut ne pas opérer de la même façon sur les tables selon le domaine dans lequel on programme ou selon ce que contient la table, il va falloir indiquer, pour chaque table, comment cette opération devra agir. La méthode opératoire que l’on va programmer et qui permettra à une opération (qui avant provoquait une erreur de script ou un effet particulier) de devenir valide sera appelée méta-méthode. La méta-méthode de la multiplication peut, par exemple, être différente selon que l’on multiplie des tables contenant des coordonnées (produit scalaire de deux vecteurs) ou que l’on multiplie des tables contenant des matrices (produit matriciel). Les méta-méthodes seront donc mémorisées dans des tables que l’on associera aux tables utilisant ces méta-méthodes. Une table ainsi associée à une autre table sera appelée méta-table.
Nous retiendrons qu'une méta-table, c’est déjà une table (comme les autres tables) mais que l’on va associer à d'autres tables grâce à une fonction appelée setmetatable. Par exemple si l’on appelle a et b deux tables et si l’on veut que b soit la méta-table de a, on écrira :
setmetatable(a,b)
Si la table b contient une méta-méthode décrivant ce que doit donner l'opérateur + si on l'applique à deux tables, alors on pourra se permettre d'écrire :
c = a + d
sans que cela ne provoque une erreur de script.
On remarque, dans l'exemple ci-dessus, que l’on a défini b comme étant la méta-table de a mais pas celle de d. Ce n’est pas grave, il suffit que la méta-méthode soit définie dans la méta-table de l'une des deux tables a ou d pour que cela marche.
Plus précisément, pour les opérateurs addition, soustraction, multiplication, division, puissance, modulo et concaténation, le Lua commence par chercher la méta-table de la table avant l'opérateur et regarde si celle-ci possède une méta-méthode concernant l'opérateur. Dans le cas contraire, le Lua cherche la méta-table de la table écrite après l'opérateur ainsi qu'une méta-méthode appropriée. Nous voyons, par conséquent, qu'une méta-méthode dans l'opérateur de gauche est prioritaire.
La façon de procéder sera, par contre, différente pour les opérateurs relationnels comme inférieur, supérieur et égal ou chaque table concernée devra avoir une méta-table avec la même méta-méthode.
Si l’on souhaite obtenir la méta-table d'une table, nous utiliserons la fonction getmetatable qui retourne la méta-table d'une table passée en paramètre (sauf s'il existe une méta-méthode disant de retourner autre chose).
Comment programmer une méta-méthode
modifierUne méta-méthode, c’est tout simplement une fonction. Nous savons que les tables peuvent contenir des fonctions. Programmer une méta-méthode revient donc à créer une fonction que l’on va mémoriser dans une méta-table. Il y a juste une petite règle à respecter. Il faut que la chaîne de caractère qui sert de clé pour indexer la fonction décrivant une méta-méthode commence par deux soulignées comme, par exemple, __index. Prenons un premier exemple simple pour comprendre comment procéder :
Nous savons que si l’on essaye d'accéder, dans une table, à une clé qui n'existe pas, nous obtenons nil. Nous allons essayer de changer ceci par une méta-méthode qui fera en sorte, qu'au lieu d'obtenir nil, on obtienne le message "(La clé spécifiée n'existe pas dans cette table)"
Nous écrirons le programme suivant qui se trouve dans le Module:Méta
local p = {}
local t = {"Piano","Boule","Tortue","Nénuphar"}
local mt = {}
function mt.__index()
return "(La clé spécifiée n'existe pas dans cette table)"
end
setmetatable(t,mt)
function p.mtable()
local reponse = " "
for i = 1,5 do
reponse = reponse.."<br>a la clé "..i..", on trouve : "..t[i]
end
return reponse
end
return p
En tapant {{#invoke:Méta|mtable}}, on obtient :
a la clé 1, on trouve : Piano
a la clé 2, on trouve : Boule
a la clé 3, on trouve : Tortue
a la clé 4, on trouve : Nénuphar
a la clé 5, on trouve : (La clé spécifiée n'existe pas dans cette table)
Sans méta-méthode se trouvant dans la méta-table mt associé à t, le programme précédent aurait provoqué une erreur de script car on essaye de concaténer t[5] qui n'existe pas (et qui vaut donc nil) à une chaîne de caractère.
On pourrait le vérifier en supprimant simplement l'instruction setmetatable(t,mt)
Si nous analysons le programme précédent, nous voyons qu’il n'y a rien de bien sensationnel si ce n'est la fonction :
function mt.__index()
return "(La clé spécifiée n'existe pas dans cette table)"
end
qui demande quelques explications.
Tout d’abord, comme nous l'avons vu dans les chapitres précédents, mt.__index() pourrait s'écrire mt["__index"]() pour mettre en évidence le fait que __index est une clé sous forme de chaîne de caractères permettant l'accès ici à une fonction se trouvant dans la table mt. Comme nous avons affaire à une méta-table, on ne dira pas que __index est une clé mais on dira que __index est un champ. Comme cela, lorsque l’on parlera de champ, on saura que l’on a affaire à une clé d'une méta-table. Attention toutefois, si l’on parle généralement de champ quand il s'agit d'une méta-table, il peut arriver que l’on utilise le mot champ au lieu du mot clé quand il s'agit d'une table ordinaire, mais c’est plus rare.
Ensuite, une question vient tout de suite à l'esprit. Comment sait-on que mt.__index est une méta-méthode concernant le comportement à adopter si l’on essaye d'accéder à une table avec une clé qui n'existe pas. En fait, les champs des méta-méthodes sont déjà préprogrammés dans le Lua. Si l’on utilise une clé qui n'existe pas dans une table, le Lua (avant de retourner erreur de script) va automatiquement commencer par regarder si la table concernée possède une méta-table et, si c’est le cas, va regarder ce qui se trouve, dans cette méta-table, à la clé __index (champ __index). Nous verrons dans un prochain exemple que si nous voulions additionner deux tables, le Lua chercherait automatiquement une méta-table associée ayant un champ se nommant __add.
Étude des champs d'une méta-table
modifierNous avons vu précédemment que le Lua avait prédéfini des champs particuliers (comme __index) pouvant être utilisés dans une méta-table pour indexer des méta-méthodes définissant le comportement particulier de la table à laquelle la méta-table est associée. Nous allons, dans ce paragraphe, passer en revue les principaux champs en essayant de donner, pour chacun d'eux, un exemple d'utilisation.
Tous les exemples se trouvent dans le Module:Champ.
Avant de commencer, nous pouvons faire une petite remarque. Il se peut que certaines méta-méthodes ne concernent qu'une seule table. Dans ce cas-là, il n’est pas nécessaire de placer la méta-méthode dans une autre table que la table concernée. On peut placer la méta-méthode dans la table concernée en la déclarant comme étant sa propre méta-table. Par exemple, pour déclarer que la table a est sa propre méta-table, on écrira setmetatable(a,a).
Le champ __index
modifierNous avons commencé à décrire ce champ sommairement plus haut pour simplifier l'exposé. Nous y revenons ici pour compléter notre description. Nous avons vu que la méta-méthode indexée par ce champ était une fonction. Dans l'exemple que nous avons donné, la fonction en question n'avait pas d'arguments.
Toutefois, il est possible d'y rajouter des arguments. Supposons que, dans le message que la méta-méthode retourne, nous voulions donner plus de précision au sujet de la clé qui n'existait pas dans la table. Il nous faut alors passer en argument la table ainsi que la clé fautive (la table est nécessaire car une méta-table peut être associée à plusieurs tables).
Le programme vu précédemment peut ainsi être amélioré :
local p = {}
local t = {"Piano","Boule","Tortue","Nénuphar"}
local mt = {}
function mt.__index(tab,cle)
return "(la clé "..cle .." n'existe pas dans cette table.)"
end
setmetatable(t,mt)
function p.vindex()
local reponse = " "
for i = 1,5 do
reponse = reponse.."<br>a la clé "..i..", on trouve : "..t[i]
end
return reponse
end
return p
tab reçoit le contenu de la table (par référence) et est donc de type table. cle reçoit la clé qui a posé problème. Nous pouvons donc, dans le retour, spécifier la clé qui a posé problème.
En tapant {{#invoke:Champ|vindex}}, on obtient :
a la clé 1, on trouve : Piano
a la clé 2, on trouve : Boule
a la clé 3, on trouve : Tortue
a la clé 4, on trouve : Nénuphar
a la clé 5, on trouve : (la clé 5 n'existe pas dans cette table.)
Question : Supposons que l’on ait prévu une méta-méthode en cas d'accès à une table avec un index qui n'existe pas et supposons qu’à titre exceptionnel, on souhaite que l'accès à la même table avec une clé qui n'existe pas retourne à nouveau nil dans un cas particulier. Est-ce possible ? Réponse : Oui, c’est possible il existe une fonction rawget telle que rawget(tab,k) est équivalent à tab[k] sauf que pour la fonction rawget, la méta-méthode est ignorée. On pourra donc utiliser cette fonction si l’on veut que l'accès à une table avec un index inexistant se comporte comme si l’on n'avait pas de méta-table. |
Le champ __add
modifierNous allons voir dans ce paragraphe comment additionner deux tables. Supposons que l’on ait deux tables B et C contenant les coordonnées de deux points B et C et supposons que nous voulions trouver un point A dont les coordonnées (mémorisées dans une table A) seront la somme des coordonnées de B et C. Par exemple, si B a pour coordonnées (3,7) et C a pour coordonnées (1,9), alors les coordonnées de A devront être (4,16).
Bien sûr, une façon simple de faire cela serait d'écrire :
local p = {}
local B = {3,7}
local C = {1,9}
function p.vadd()
local A = {}
A[1] = B[1] + C[1]
A[2] = B[2] + C[2]
return "Le point A à pour coordonnées "..A[1].." et "..A[2]
end
return p
et ça marche !
Le problème, c’est que l’on risque d’avoir toute une suite de calculs longs et fastidieux à faire et l’on aimerait bien simplifier l'écriture du programme ainsi :
local p = {}
local B = {3,7}
local C = {1,9}
function p.vadd()
local A = {}
A = B + C
return "Le point A à pour coordonnées "..A[1].." et "..A[2]
end
return p
Mais là, nous obtenons une erreur de script car B + C n’est pas défini a priori !
Que faire ? En fait, c’est très simple. Nous allons tout d’abord déclarer une table que nous appellerons point. Nous allons ensuite rattacher cette table aux tables B et C en tant que méta-table grâce aux instructions setmetatable(B,Point) et setmetatable(C,Point). On remarque au passage l'aspect naturel de ces deux déclarations car cela revient à définir A et B comme étant des points. Ensuite, nous allons mettre dans la table point une méta-méthode indiquant comment additionner les tables ayant la table point comme méta-table. Autrement dit nous allons définir dans la table point une fonction Point.__add.
Nous compléterons donc le programme précédent ainsi :
local p = {}
local B = {3,7}
local C = {1,9}
local Point = {}
setmetatable(B,Point)
setmetatable(C,Point)
function Point.__add(s,t)
local u = {}
u[1] = s[1] + t[1]
u[2] = s[2] + t[2]
return u
end
function p.vadd()
local A = {}
A = B + C
return "Le point A à pour coordonnées "..A[1].." et "..A[2]
end
return p
En tapant {{#invoke:Champ|vadd}}, on obtient : Le point A à pour coordonnées 4 et 16
Nous voyons que ça marche ! Nous n'avons plus d'erreur de script !
Le champ __sub
modifierCe champ permet de décrire comment faire la différence de deux tables. L'utilisation est similaire au champ __add, il suffit de remplacer + par -. Nous écrirons :
local p = {}
local B = {3,7}
local C = {1,9}
local Point = {}
setmetatable(B,Point)
setmetatable(C,Point)
function Point.__sub(s,t)
local u = {}
u[1] = s[1] - t[1]
u[2] = s[2] - t[2]
return u
end
function p.vsub()
local A = {}
A = B - C
return "Le point A à pour coordonnées "..A[1].." et "..A[2]
end
return p
En tapant {{#invoke:Champ|vsub}}, on obtient : Le point A à pour coordonnées 2 et -2
Le champ __mul
modifierCe champ permet de décrire comment multiplier deux tables entre elles. Si on n'avait guère de choix quand il s'agissait d'additionner ou de soustraire deux tables, la situation est différente pour la multiplication. En effet, ne serait-ce que dans le domaine des mathématiques, on peut envisager plusieurs façons de multiplier deux tables. Si les tables contiennent les coordonnées des vecteurs, on peut envisager d’en faire le produit scalaire ou le produit vectoriel. Si les tables mémorisent des matrices, on peut envisager d’en faire le produit matriciel. Plus généralement, si les tables sont des tenseurs, on peut faire des produits tensoriels. Nous voyons que la notion de méta-méthode acquiert une certaine importance dans le cas de la multiplication de deux tables. Dans ce paragraphe, nous verrons comment programmer une méta-méthode faisant le produit scalaire de deux vecteurs (nous étudierons le produit vectoriel et le produit matriciel en exercice).
Le produit scalaire de deux vecteurs de coordonnés (a,b) et (c,d) est le nombre ac+bd. Autrement dit c’est la somme du produit des abscisses et du produit des ordonnées. |
Nous allons donc créer une méta-méthode réalisant cette opération.
local p = {}
local B = {3,7}
local C = {1,9}
local Point = {}
setmetatable(B,Point)
setmetatable(C,Point)
function Point.__mul(s,t)
local p
p = s[1]*t[1]+s[2]*t[2]
return p
end
function p.scalaire()
local sca
sca = B*C
return "Le produit scalaire des vecteurs B et C est "..sca
end
return p
Dans ce dernier programme, nous remarquons une particularité intéressante, c’est que le produit de deux tables ne nous a pas donné une table mais un nombre. Avec les méta-méthodes, nous pouvons faire en sorte qu'une opération entre deux tables nous donne quelque chose qui n’est pas une table.
En tapant {{#invoke:Champ|scalaire}}, on obtient : Le produit scalaire des vecteurs B et C est 66
Le champ __div
modifierNous serions tenté de dire : "Dans ce paragraphe, nous allons voir comment diviser deux tables". En fait, il est rare d’avoir à diviser deux tables entre elles. Par conséquent, nous allons plutôt voir dans ce paragraphe comment diviser une table par un nombre, ce qui est plus utile comme par exemple dans l'opération vue plus haut dans ce chapitre.
C'est-à-dire que nous allons enfin voir, à titre d'exemple, comment calculer le milieu de deux points.
local p = {}
local B = {3,7}
local C = {1,9}
local Point = {}
setmetatable(B,Point)
setmetatable(C,Point)
function Point.__add(s,t)
local u = {}
u[1] = s[1] + t[1]
u[2] = s[2] + t[2]
return u
end
function Point.__div(s,a)
local q = {}
q[1] = s[1]/a
q[2] = s[2]/a
return q
end
function p.milieu()
local I = {}
local S = {}
S = B + C
setmetatable(S,Point)
I = S/2
return "Le milieu des deux points B et C a pour coordonnées "..I[1].." et "..I[2]
end
return p
En tapant {{#invoke:Champ|milieu}}, on obtient : Le milieu des deux points B et C a pour coordonnées 2 et 8
Nous avons écrit : local I = {}
local S = {}
S = B + C
: setmetatable(S,Point)
I = S/2
Alors que le bon sens nous aurait plutôt poussé à écrire : local I = {}
local S = {}
setmetatable(S,Point)
S = B + C
I = S/2
Pourtant, cette dernière façon d'écrire le programme provoque une erreur de script comme si l'opération S = B + C faisait perdre le fait que S a pour méta-table Point.
|
Le champ __concat
modifierDans ce paragraphe, nous allons voir comment concaténer deux tables. Là aussi, nous pouvons imaginer plusieurs façons de concaténer deux tables. Nous allons en voir une ici, à titre d'exemple, et nous en verrons une autre dans la page d'exercices associée.
local p = {}
local B = {3,7}
local C = {1,9}
local Point = {}
setmetatable(B,Point)
setmetatable(C,Point)
function Point.__concat(s,t)
local q = {}
q[1] = tonumber(s[1]..t[1])
q[2] = tonumber(s[2]..t[2])
return q
end
function p.concatene()
local S = {}
S = B..C
return "En concatènant les deux tables B et C, nous avons obtenu : ("..S[1]..","..S[2]..")."
end
return p
return p
En tapant {{#invoke:Champ|concatene}}, on obtient : En concatènant les deux tables B et C, avons obtenu : (31,79).
Le champ __pow
modifierCe champ nous permet d'accéder à une méta-méthode permettant d'élever une table à une certaine puissance. Pour simplifier, l'exemple ci-dessous se contente d'élever chaque nombre de la table à la puissance indiquée. On pourrait, pour compliquer, imaginer une méta-méthode qui élève une matrice carrée à une certaine puissance entière.
local p = {}
local B = {3,7}
local Point = {}
setmetatable(B,Point)
function Point.__pow(s,e)
local q = {}
q[1] = s[1]^e
q[2] = s[2]^e
return q
end
function p.puissance()
local S = {}
S = B^2
return "En élevant la table B à la puissance 2, nous obtenons : ("..S[1]..","..S[2]..")."
end
return p
return p
En tapant {{#invoke:Champ|puissance}}, on obtient : En élevant la table B à la puissance 2, nous obtenons : (9,49).
Le champ __mod
modifierLa fonction modulo donne le reste de la division par un nombre donné. L'exemple ci-dessous nous donne une table contenant les restes des divisions des nombres de la table donnée par un nombre donnée.
local p = {}
local B = {3,7}
local Point = {}
setmetatable(B,Point)
function Point.__mod(s,m)
local q = {}
q[1] = s[1]%m
q[2] = s[2]%m
return q
end
function p.modulo()
local S = {}
S = B%3
return "En calculant la classe modulo 3 de la table B, nous obtenons : ("..S[1]..","..S[2]..")."
end
return p
return p
En tapant {{#invoke:Champ|modulo}}, on obtient : En calculant la classe modulo 3 de la table B, nous obtenons : (0,1).
Le champ __unm
modifierCe champ nous fournit une méta-méthode pour interpréter ce que peut donner une table affectée du signe -. Dans notre exemple, on obtiendra une table ou les signes respectifs de toutes ses valeurs sont inversés.
local p = {}
local B = {3,7}
local Point = {}
setmetatable(B,Point)
function Point.__unm(s,m)
local q = {}
q[1] = -s[1]
q[2] = -s[2]
return q
end
function p.negation()
local S = {}
S = -B
return "En calculant l'opposée de la table B, nous obtenons : ("..S[1]..","..S[2]..")."
end
return p
En tapant {{#invoke:Champ|negation}}, on obtient : En calculant l'opposée de la table B, nous obtenons : (-3,-7).
Le champ __eq
modifierLa comparaisons a == b est valable si a et b sont des tables. a == b retournera true si la table a contient les mêmes éléments que la table b. Mais on peut souhaiter un autre comportement.
Ce champ nous permet d'interpréter ce que peut donner l'opérateur relationnel == placé entre deux tables. Par exemple, comme c’est le cas dans notre exemple, on peut vouloir tester l'égalité approximative entre deux tables. Il y a une différence entre ce champ et les champs vus précédemment, c’est que les deux tables concernées doivent avoir la même méta-méthode dans leur méta-table. Le plus simple est en fait qu’elles aient la même méta-table. Il en sera de même pour les autres opérateurs relationnels <, >, <=, >=.
local p = {}
local B = {3,7}
local C = {1,9}
local Point = {}
setmetatable(B,Point)
setmetatable(C,Point)
function Point.__eq(s,t)
if math.abs(s[1] - t[1]) < 0.1 and math.abs(s[2] - t[2]) < 0.1 then
return true
else
return false
end
end
function p.egal()
if B == C then
return "Les tables B et C sont égales."
else
return "Les tables B et C sont différentes."
end
end
return p
En tapant {{#invoke:Champ|egal}}, on obtient : Les tables B et C sont différentes.
Comme pour le champs __index, on peut souhaiter que la méta-méthode soit ignorée dans certain cas. On utilisera alors la fonction rawequal utilisant deux paramètres sous la forme rawequal(a,b) qui est équivalent à a == b mais qui ignore la méta-méthode de ce paragraphe.
Le champ __le
modifierCe champ nous permet d'interpréter ce que peuvent donner les opérateurs relationnels >= et <= placés entre deux tables. En principe, comme c’est le cas dans notre exemple, il s'agit de tester si une table est inférieure ou égale ou supérieure ou égale à l'autre. Toutefois, comme il y a plusieurs nombres dans la table, on peut avoir le choix sur la méta-méthode. Dans notre exemple, on dira qu'une table est supérieure ou égale à une autre table si la somme des nombres appartenant à la première table est supérieure ou égale à la somme des nombres appartenant à la deuxième table. Il y a une différence entre ce champ et les champs non relationnels, c’est que les deux tables concernées doivent avoir la même méta-méthode dans leur méta-table. Le plus simple est en fait qu’elles aient la même méta-table. Il en sera de même pour les autres opérateurs relationnels <, >, ==.
local p = {}
local B = {3,7}
local C = {1,9}
local Point = {}
setmetatable(B,Point)
setmetatable(C,Point)
function Point.__le(s,t)
if s[1] + s[2] >= t[1] + t[2] then
return true
else
return false
end
end
function p.superieur()
if B >= C then
return "La table B est supérieure ou égale à la table C."
else
return "La table B est strictement infèrieure à la table C."
end
end
return p
En tapant {{#invoke:Champ|superieur}}, on obtient : La table B est supérieure ou égale à la table C.
Le champ __lt
modifierCe champ nous permet d'interpréter ce que peuvent donner les opérateurs relationnels > et < placés entre deux tables. En principe, comme c’est le cas dans notre exemple, il s'agit de tester si une table est inférieure ou supérieure à l'autre. Toutefois, comme il y a plusieurs nombres dans la table, on peut avoir le choix sur la méta-méthode. Dans notre exemple, on dira qu'une table est inférieure à une autre table si la somme des nombres appartenant à la première table est inférieure à la somme des nombres appartenant à la deuxième table. Il y a une différence entre ce champ et les champs non relationnels, c’est que les deux tables concernées doivent avoir la même méta-méthode dans leur méta-table. Le plus simple est en fait qu’elles aient la même méta-table. Il en sera de même pour les autres opérateurs relationnels <=, >=, ==.
local p = {}
local B = {3,7}
local C = {1,9}
local Point = {}
setmetatable(B,Point)
setmetatable(C,Point)
function Point.__lt(s,t)
if s[1] + s[2] < t[1] + t[2] then
return true
else
return false
end
end
function p.inferieur()
if B < C then
return "La table B est strictement infèrieure à la table C."
else
return "La table B est supérieure ou égale à la table C."
end
end
return p
En tapant {{#invoke:Champ|inferieur}}, on obtient : La table B est supérieure ou égale à la table C.
Le champ __ipairs
modifierCe champ permet de donner une alternative à la fonction ipairs. Si ce champ existe, l'appel à la fonction ipairs nous retournera la fonction itérative, la table, et l'instruction d'arrêt prévu par la méta-méthode __ipairs.
Dans l'exemple qui suit, la méta-méthode associée à la fonction ipairs est programmée de façon à sélectionner un objet sur deux dans la table souk. L'appel à la fonction ipairs, dans la fonction p.liste, nous retournera donc une fonction itérative suivant qui incrémente la clé de deux unités au lieu d'une.
local p = {}
local souk = {"flute", "pipo", "manche à balaie", "serpière", "jeu de cartes", "coton tige", "tourne vis", "rateau", "stylo", "poupée"}
local entrepot = {}
setmetatable(souk,entrepot)
function suivant(tab,n)
if n == nil then n = -1 end
if tab[n+2] == nil then
return nil,nil
else
return n+2,tab[n+2]
end
end
function entrepot.__ipairs(t)
return suivant,t,nil
end
function p.liste()
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
return p
En tapant {{#invoke:Champ|liste}}, on obtient :
à la clé numéro 1 se trouve l’objet flute.
à la clé numéro 3 se trouve l’objet manche à balaie.
à la clé numéro 5 se trouve l’objet jeu de cartes.
à la clé numéro 7 se trouve l’objet tourne vis.
à la clé numéro 9 se trouve l’objet stylo.
Le champ __metatable
modifierLa grande nouveauté pour ce champ est qu’il n'indexe pas une fonction. Ce champ indexe une valeur qui peut être une chaîne de caractères, un nombre, un booléen ou une table. Dans notre exemple, ce champ indexe la chaîne de caractère "Coordonnées" pour indiquer que la méta-table est associée à des tables qui contiennent des coordonnées. Lorsqu'on utilise la fonction getmetatable sur une table ayant pour méta-table, une table contenant ce champ, au lieu d'obtenir la méta-table de la table, on obtiendra le message "Coordonnées".
local p = {}
local B = {3,7}
local Point = {}
setmetatable(B,Point)
Point.__metatable = "Coordonnées"
function p.obtenir()
return getmetatable(B)
end
return p
En tapant {{#invoke:Champ|obtenir}}, on obtient : Coordonnées
Le champ __tostring
modifierCe champ indexe une méta-méthode indiquant ce que doit donner la fonction tostring si on l'applique à une table (qui a une méta-table contenant la méta-méthode en question). Dans notre exemple, on obtiendra une chaîne de caractères représentant la table formée des deux coordonnées séparées par une virgule et se trouvant entre parenthèses.
local p = {}
local B = {3,7}
local Point = {}
setmetatable(B,Point)
function Point.__tostring(q)
return "("..q[1]..","..q[2]..")"
end
function p.convertir()
return tostring(B)
end
return p
En tapant {{#invoke:Champ|convertir}}, on obtient : (3,7)
Le champ __call
modifierCe champ permet de transformer une table en fonction lorsqu'on l'emploie en utilisant la syntaxe d'une fonction. Dans l'exemple ci-dessous, la table tb devient une fonction tb qui ajoute 3 à son argument. tb(17) donne alors 20.
local p = {}
local newtable = {}
function newtable.__call(tab,x)
return x + 3
end
function p.appel()
local tb = {}
setmetatable(tb,newtable)
return tb(17)
end
return p
En tapant {{#invoke:Champ|appel}}, on obtient : 20
Le champ __newindex
modifierCe champ permet de rendre possible une assignation dans une table, pour une clé donnée, uniquement s'il y a déjà une valeur pour cette clé. Autrement dit, on peut modifier des valeurs déjà existantes dans la table mais on ne peut pas en ajouter de nouvelles. Dans l'exemple ci-dessous, nous avons prévu deux fonctions p.assigne1 et p.assigne2. Dans la fonction p.assigne1, nous voyons que nous avons déclaré une table tb sans l'initialiser. L'instruction tb[2] = "Framboise" est donc sans effet. Nous le constatons en retournant le type de tb[2] qui est nil indiquant que l'assignation a été inopérante. Dans la fonction p.assigne2, nous voyons que nous avons déclaré une table tb en l'initialisant. L'instruction tb[2] = "Framboise" est donc opérationnelle. Nous pouvons le constater en retournant la valeur de tb[2] qui est Framboise indiquant que l'assignation a été faîte.
local p = {}
local newtable = {}
function newtable.__newindex(tab,cle,valeur)
return "Bienvenue dans la table"
end
function p.assignation1()
local tb = {}
local reponse
setmetatable(tb,newtable)
tb[2] = "Framboise"
return type(tb[2])
end
function p.assignation2()
local tb = {"Moustique","Montagne","boulet"}
local reponse
setmetatable(tb,newtable)
tb[2] = "Framboise"
return tb[2]
end
return p
En tapant {{#invoke:Champ|assignation1}}, on obtient : nil
En tapant {{#invoke:Champ|assignation2}}, on obtient : Framboise
Comme pour le champs __index, on peut souhaiter que la méta-méthode soit ignorée dans certain cas. On utilisera alors la fonction rawset utilisant trois paramètres sous la forme rawset( tab, k, v ) qui est équivalente à tab[k] = v mais qui ignore la méta-méthode de ce paragraphe.
Le champ __mode
modifierCe champ permet de mettre des références faibles dans une table. Si ce champ est utilisé, l'espace alloué à une valeur de la table peut être récupéré par le ramasse miette. Les valeurs stockées dans la table peuvent ainsi être perdues aléatoirement. Si le champ indexe la chaîne "k", les clés de la table peuvent être nettoyées. Si le champ indexe la chaîne "v", les valeurs de la table peuvent être nettoyées.
Il est difficile de donner un exemple pour ce champ. Nous en resterons donc là !
local p = {}
return p
En tapant {{#invoke:Champ|faible}}, on obtient : table
Librairies Scribunto
Les librairies Scribunto apporte un ensembles de fonctions spécialisées plus particulièrement adaptés aux projets de la fondation Wikimédia.
Présentation
modifierLes librairies Scribunto sont constituées d'ensembles d'objets, chacun des ensembles étant spécialisé dans un domaine particulier. Par exemple, la librairie frame est spécialisée dans l'interface entre l'utilisateur et le programme. La librairie Title sera spécialisée dans l'accès aux pages d'un Wiki, etc.
Les librairies Scribunto sont des ajouts qui ne font pas partie du Lua standard. Elles ont été écrites spécialement pour les projets Wikimédia. Elles permettent de faciliter l'écriture de programmes en Lua concernant la gestion des projets.
Toutes ces librairies sont constituées d'une table contenant principalement des fonctions mais pouvant aussi contenir d'autres objets comme des chaînes de caractères, des sous-tables, des booléens etc.
Toutes les librairies Scribunto se trouvent dans une table principale qui se nomme mw. Pour accéder à une librairie, il suffira donc de taper mw."nom de la librairie". Supposons que l’on veuille accéder à la fonction indexée par new se trouvant dans la librairie message, il nous suffira de taper mw.message.new(paramètres) (nous rappelons que new est en fait un index de la table mw.message, message étant lui même un index de la table mw)
Une particularité de la plupart des librairies Scribunto est de travailler sur une ou plusieurs tables fétiches de la librairie considérée. Ces tables porteront le nom d'objet suivi du nom de la librairie. par exemple la librairie Title travaillera sur des objets Title. Les tables constituant les objets fétiches d'une librairie contiennent, bien sûr, elles aussi, des fonctions ou autre chose (sous-tables, chaînes de caractères, booléens,etc.).
Supposons que, dans le cadre de la librairie message, on ait créé un objet message du nom de cocorico par exemple. Cet objet message va contenir tout un ensemble de fonctions parmi lesquelles se trouve la fonction indexée par plain. Il y a deux façons d'accéder à cette fonction. On a tout d’abord la façon habituelle, c'est-à-dire cocorico.plain(paramètres). Mais il existe une autre façon qui consiste à écrire mw.message:plain(paramètres). On remarque la présence de : dans mw.message:plain qui indique que l’on va chercher la fonction, non pas dans la table mw.message, mais dans un objet message dont le nom figure parmi les paramètres (ou pas, s'il n'y a pas de confusion possible sur l’objet visé comme l'objet frame de la librairie frame)
Nous venons de voir qu’il existe deux façons d'accéder à une fonction se trouvant dans un objet fétiche d'une librairie. Si l’on reprend l'exemple précédent, on a : cocorico.plain(paramètres) ou l’on a : mw.message:plain(paramètres). Par convention, pour différencier ces deux façons, nous dirons que la notation cocorico.plain désigne une fonction alors que la notation mw.message:plain désigne une méthode (même si une méthode n'est rien d’autre qu'une fonction). Comme, le plus souvent, on accède aux fonctions se trouvant dans les objets fétiches avec la notation méthode, on dira que les fonctions se trouvant dans les objets fétiches sont des méthodes. Si, par exemple, nous disons que dans la librairie langage, il y a 9 fonctions et 20 méthodes, nous voulons dire par là que 9 fonctions sont indexées directement dans la table mw.langage et 20 fonctions sont indexées dans un objet langage qui sera créé dans le cadre de cette librairie.
Ce que nous venons de dire est très théorique et comporte des exceptions. Nous verrons, par exemple, qu’il n'existe pas de librairie frame mais qu’il existe malgré tout un objet frame. Nous verrons aussi que les méthodes, dans la librairie HTML, n'apparaissent pas dans un objet html.
Fonctions et tables constituant les librairies Scribunto
modifierToutes les librairies Scribunto se trouvent dans la table mw
. Nous commencerons donc par visualiser le contenu de cette table grâce au programme suivant :
local p = {}
function p.visualisation(frame)
reponse = ""
for index, objet in pairs(mw) do
reponse = reponse.."<br />À la clé "..index..", on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Scribunto|visualisation}} nous donne :
À la clé incrementExpensiveFunctionCount, on trouve un objet de type : function
À la clé log, on trouve un objet de type : function
À la clé addWarning, on trouve un objet de type : function
À la clé getLanguage, on trouve un objet de type : function
À la clé getCurrentFrame, on trouve un objet de type : function
À la clé loadJsonData, on trouve un objet de type : function
À la clé logObject, on trouve un objet de type : function
À la clé allToString, on trouve un objet de type : function
À la clé executeFunction, on trouve un objet de type : function
À la clé loadData, on trouve un objet de type : function
À la clé hash, on trouve un objet de type : table
À la clé title, on trouve un objet de type : table
À la clé uri, on trouve un objet de type : table
À la clé clone, on trouve un objet de type : function
À la clé text, on trouve un objet de type : table
À la clé language, on trouve un objet de type : table
À la clé message, on trouve un objet de type : table
À la clé wikibase, on trouve un objet de type : table
À la clé site, on trouve un objet de type : table
À la clé ustring, on trouve un objet de type : table
À la clé isSubsting, on trouve un objet de type : function
À la clé dumpObject, on trouve un objet de type : function
À la clé ext, on trouve un objet de type : table
À la clé html, on trouve un objet de type : table
À la clé getContentLanguage, on trouve un objet de type : function
Nous voyons que nous avons 9 tables constituant 9 librairies spécialisées, mais nous voyons aussi apparaître 12 fonctions. Nous allons étudier les 12 fonctions que nous désignerons sous le terme "fonctions de base" dans le paragraphe suivant. Nous présenterons ensuite les 9 tables constituant les 9 librairies spécialisées qui contiennent chacune des fonctions spécialisées. Dans ce chapitre, nous n'étudierons pas les 9 librairies spécialisées pour ne pas surcharger de façon colossale ce chapitre. Cette étude sera effectuée dans d'autres chapitres.
Fonctions de base
modifiermw.allToString
modifiermw.allToString( ... )
Appelle tostring() sur tous ses paramètres, puis concatène les résultats avec une tabulation comme séparateur.
mw.clone
modifiermw.clone( value )
Crée une copie de la valeur. Toutes les tables (et leurs méta-tables) sont reconstruites de zéro. Les fonctions restent partagées quoi qu’il en soit.
mw:executeFunction
modifiermw.getCurrentFrame
modifiermw.getCurrentFrame()
Retourne l'objet frame courant.
mw:getContentLanguage
modifiermw:getLanguage
modifier
mw.incrementExpensiveFunctionCount
modifiermw.incrementExpensiveFunctionCount()
Ajoute un au compteur des « parser-function coûteuses », et génère une exception si ce compteur dépasse la limite (voir $wgExpensiveParserFunctionLimit).
mw.isSubsting
modifiermw.isSubsting()
Renvoie true si le #invoke
courant est en train d’être « substé », false dans le cas contraire. Voir Retourner du texte plus haut pour connaitre la différence entre un #invoke
substé ou non.
mw.loadData
modifiermw.loadData( module )
Parfois un module a besoin de grandes tables de données. Par exemple, un module générique de conversion d'unités devrait avoir une grande table contenant toutes les unités connues et leurs facteurs de conversion. Et parfois ces modules vont être utilisés de nombreuses fois dans une même page. Interpréter de grandes tables de données pour chaque {{#invoke:}}
peut prendre un temps non négligeable. Pour éviter ce problème, la fonction mw.loadData()
existe.
mw.loadData
fonctionne comme require()
avec les différences suivantes :
- Le module n'est évalué qu'une fois par page, plutôt qu'une fois par appel à
{{#invoke:}}
. - Le module n’est pas enregistré dans
package.loaded
. - La valeur retournée par le module chargé doit être une table. Les autres types ne sont pas gérés.
- La table retournée (et ses sous-tables) peut contenir des booléens, des nombres, des chaînes et d'autres tables. Les autres types, en particulier les fonctions, ne sont pas autorisés.
- La table retournée (et toutes ses sous-tables) ne doit pas avoir de méta-tables.
- Toutes les clés de la table doivent être des booléens, des nombres ou des chaînes.
- La table réellement retournée par
mw.loadData()
a des méta-méthodes qui fournissent un accès en lecture seul à cette table. Dans la mesure où elle ne contient pas directement les données,pairs()
etipairs()
fonctionnent mais les autres méthodes, y comprisInitiation au Lua avec Scribunto/Fonctions basiques#value
,next()
, et les fonctions de la librairie Table, ne fonctionneront pas correctement.
Dans l'hypothétique module évoqué plus haut, le module pourrait être « Module:Convert » et les données pourraient être stockées dans « Module:Convert/data ». Le module utiliserait local data = mw.loadData( 'Module:Convert/data' )
pour charger efficacement les données.
mw.dumpObject
modifiermw.dumpObject( object )
Renvoie une représentation lisible par un humain de l’objet object
, sous forme de chaîne.
mw.log
modifiermw.log( ... )
Envoie ses paramètres à mw.allToString() puis ajoute le résultat au tampon des enregistrements (log).
Dans la console de debug, la fonction print()
est un alias de cette fonction.
mw.logObject
modifiermw.logObject( object )
mw.logObject( object, prefix )
Appelle mw.dumpObject() et concatène la chaîne résultante au « log buffer » (traduction ?). Si prefix
est renseigné, il sera ajouté au « log buffer » suivi d'un signe égal avant que la chaîne produite soit concaténée (i.e. le texte loggé sera "prefix = object-string").
Les tables constituant les librairies Scribunto
modifierLa table mw.html
modifierCette table héberge les éléments de la librairie HTML. Pour éviter de surcharger ce chapitre, l'étude de la librairie HTML sera faite dans le chapitre 15.
La table mw.language
modifierCette table héberge les éléments de la librairie Language. Pour éviter de surcharger ce chapitre, l'étude de la librairie langage sera faite dans le chapitre 16.
La table mw.text
modifierCette table héberge les éléments de la librairie Text. La librairie text fournit quelques fonctions utiles de traitement de texte absentes de la librairie String et de la librairie Ustring. Ces fonctions peuvent fonctionner avec les caractères UTF-8.
Les fonctions de cette librairie ont déjà été étudiées dans le chapitre 6 pour deux raisons : d'une part, pour alléger le présent chapitre qui est déjà volumineux et ensuite parce que ces fonctions agissent sur les chaînes de caractères, ce qui est l’objet du chapitre 6.
La table mw.ustring
modifierCette table héberge les éléments de la librairie Ustring. Les fonctions de cette librairie ont déjà été étudiées dans le chapitre 6 pour deux raisons : d'une part, pour alléger le présent chapitre qui est déjà volumineux et ensuite parce que ces fonctions agissent sur les chaînes de caractères, ce qui est l’objet du chapitre 6.
La table mw.message
modifierCette table héberge les éléments de la librairie Message. Pour éviter de surcharger ce chapitre, l'étude de la librairie message sera faite dans le chapitre 17
La table mw.site
modifierCette table héberge les éléments de la librairie Site. Pour éviter de surcharger ce chapitre, l'étude de la librairie site sera faite dans le chapitre 18
La table mw.title
modifierCette table héberge les éléments de la librairie Title. Pour éviter de surcharger ce chapitre, l'étude de l’objet Title sera faite dans le chapitre 13
La table mw.ext
modifierCette table semble n'avoir rien à voir avec notre propos. Si l’on regarde ce qu'elle contient, on y trouve les deux sous-tables :
- mw.ext.ParserFunctions
- mw.ext.TitleBlacklist
La table mw.uri
modifierCette table héberge les éléments de la librairie Uri. Pour éviter de surcharger ce chapitre, l'étude de l’objet URI sera faite dans le chapitre 14
L'objet Frame
modifierOn aurait pu s'attendre à voir apparaître une table mw.frame dans la table mw. Mais cela n’est pas arrivé. On peut admettre l’existence d'une table virtuelle mw.frame puisqu’il existe un objet frame. L'objet frame est l'interface des paramètres passés au {{#invoke:}}
, ainsi que l'interface au parseur.
Pour éviter de surcharger ce chapitre, l'étude de l’objet Frame sera faite dans le chapitre 12
L'objet Frame
Ce paragraphe étudie plus en détail l’objet frame que l’on peut considérer comme étant l'interface avec le programme. C'est l’objet frame qui reçoit les paramètres que l’on envoie au programme grâce à la commande #invoke.
Nature de l’objet frame
modifierUne remarque que l’on peut faire à propos de l’objet frame et de la façon de l’utiliser comme l'accès aux arguments frame.args[1], frame.args[2], etc. est la ressemblance avec les notations relatives aux tables. frame serait une table et frame.args que l’on pourrait aussi noter frame["args"] serait une table, indexée par la chaîne de caractères "args", se trouvant dans la table frame. Par contre frame.args serait une table à accès numérique. C'est pour cela que l’on note ses arguments frame.args[1], frame.args[2], etc.
En réalité, ce que l’on vient de dire n’est pas tout à fait vrai. À l'appel d'un module avec #invoke, la table frame n’est pas créée et remplie tel que l’on vient de le dire. Quand l’on fait appel à un élément de la table numérique frame.arg, une méta-méthode est activée pour demander l’objet souhaité au logiciel MediaWiki. La table frame est en réalité une méta-table remplie de méta-méthodes dont la fonction est de simuler l’existence d'une table qui contiendrait les éléments décrits plus haut alors, qu'en réalité, ils sont demandés au fur et à mesure des besoins au logiciel MediaWiki.
Comme "frame" n’est pas une vraie table, on n’est pas assuré que toutes les fonctions agissant sur les tables vont fonctionner correctement. Nous devons donc connaître la liste des fonctions qui fonctionnent correctement et la liste des fonctions qui ne fonctionnent pas correctement, ces deux listes étant susceptibles d’être modifiées au fil des versions de Lua et de Scribunto.
Les fonctions sur lesquelles, on peut compter actuellement sont ipairs et pairs.
Nous allons donc donner un exemple simple utilisant la fonction ipair.
Dans le Module:Frame, écrivons une fonction p.evalue qui compte combien il y a d'arguments contenant une valeur numérique et combien il y a d'arguments contenant une chaîne de caractères.
local p = {}
function p.evalue(frame)
local reponse = " "
local nombre,chaine = 0,0
for index, objet in ipairs(frame.args) do
if tonumber(objet) == nil then
chaine = chaine + 1
else
nombre = nombre + 1
end
end
reponse = reponse.."Il y a "..nombre.." nombres et "..chaine.." chaines de caractères."
return reponse
end
return p
{{#invoke:Frame|evalue|Laura|54|76|Mouchoir|17|Schtroumpfs|11|2}}, nous donne : Il y a 5 nombres et 3 chaines de caractères.
Arguments avec clé sous forme de chaîne de caratères
modifierJusqu'à maintenant, nous avons déclaré, dans la commande #invoke, les arguments à la suite les uns des autres comme on déclare les objets d'une table à clé numérique. Le premier argument s'associant automatiquement à la clé 1, le deuxième à la clé 2 et ainsi de suite. Toutefois, de même que l’on a vu que, dans les tables, il est possible de créer des clés sous forme de chaîne de caractères, nous allons voir qu’il en est de même pour la pseudo-table frame.args. Pour cela, il suffit de déclarer, dans la commande #invoke, les arguments avec clé sous forme de chaîne de caractères comme on le ferait pour une table.
Par exemple, on écrira : {{#invoke:''Module''|''fonction''|fleur=rose}}
Il est à noter que la déclaration : {{#invoke:''Module''|''fonction''|["fleur"]=rose}} n’est pas correcte car ["fleur"] sera interprété comme la chaîne de caractère "["fleur"]" (voir l'exemple ci-dessous avec ("personnage"]).
Écrivons un exemple pour voir si cela marche bien !
Dans le Module:Frame, écrivons une fonction p.renvoie qui nous renvoie les clés utilisés avec les objets correspondants.
local p = {}
function p.renvoie(frame)
local reponse = " "
for index, objet in pairs(frame.args) do
reponse = reponse.."<br>À la clé "..index..", il y a l’objet : "..objet
end
return reponse
end
return p
{{#invoke:Frame|renvoie|prénom=Laura|nombre=54|76|Mouchoir|17|["personnage"]=Schtroumpfs|nombre premier=11|2}}, nous donne :
À la clé 1, il y a l’objet : 76
À la clé 2, il y a l’objet : Mouchoir
À la clé 3, il y a l’objet : 17
À la clé 4, il y a l’objet : 2
À la clé prénom, il y a l’objet : Laura
À la clé nombre, il y a l’objet : 54
À la clé nombre premier, il y a l’objet : 11
À la clé ["personnage"], il y a l’objet : Schtroumpfs
Dans le programme précédent, nous avons utilisé la fonction pairs. Si nous avions utilisé la fonction ipairs, tous les objets dont la clé est une chaîne de caractères auraient été ignorés.
Fonctions en relation avec l’objet frame
modifierTous les exemples de ce paragraphe se trouvent dans le Module:Frame.
Les fonctions que nous allons étudier dans ce paragraphe se trouvent dans l’objet frame. Nous commencerons donc par écrire une fonction p.visualisation permettant de les visualiser.
(Ici, contrairement aux paragraphes précédents, nous visualisons ce qui se trouve dans frame et pas ce qui se trouve dans frame.args.)
local p = {}
function p.visualise(frame)
reponse = ""
for index, objet in pairs(frame) do
reponse = reponse.."<br />À la clé "..index..", on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Frame|visualise}} nous donne :
À la clé newTemplateParserValue, on trouve un objet de type : function
À la clé getParent, on trouve un objet de type : function
À la clé argumentPairs, on trouve un objet de type : function
À la clé extensionTag, on trouve un objet de type : function
À la clé callParserFunction, on trouve un objet de type : function
À la clé preprocess, on trouve un objet de type : function
À la clé getTitle, on trouve un objet de type : function
À la clé args, on trouve un objet de type : table
À la clé newParserValue, on trouve un objet de type : function
À la clé expandTemplate, on trouve un objet de type : function
À la clé getArgument, on trouve un objet de type : function
À la clé newChild, on trouve un objet de type : function
En dehors de la clé args qui référence la table contenant les arguments transmis par la commande #invoke, nous voyons que nous disposons de 11 fonctions que nous allons étudier en détail ci-dessous.
Une petite remarque que l’on peut faire ici est que la notation des fonctions diffère légèrement de ce que l’on avait mentionné auparavant. En effet, au lieu de noter, par exemple, la deuxième fonction de la liste frame.getParent (avec .), nous la noterons frame:getParent (avec :). Cette différence de notation tient au type de propriété à laquelle on accède. Le point permet d'accéder à la propriété définie dans l'objet frame tandis que le deux-points permet d'exécuter une fonction définie sur l'objet.
Autre remarque, les fonctions des librairies sont accessibles comme des propriétés. C'est pourquoi on utilise le point pour accéder à une fonction d'une librairie et non le deux-points car la librairie n’est pas instanciée (n'est pas un objet) : string.len(s). Quand on a à faire à un objet, l'appel à la méthode se fait par les deux-points : s:len().
Comme d'habitude, les fonctions ne seront pas étudiées dans l’ordre !
frame:getParent
modifierCette fonction permet de connaître, à l'intérieur du module, les arguments du modèle qui a appelé le module où se trouve la fonction frame:getParent.
La fonction frame:getParent retourne une table contenant une table, indexée par la chaîne de caractère "args", qui contient tous les paramètres passés au modèle. Par exemple, si la fonction frame:getParent se trouve dans un module placé dans un modèle dont l'appel se fait ainsi :
{{Essai
| titre = stat
| compte = ouvert
}}
Et si dans le module, nous avons l'instruction :
local model = frame:getParent()
Alors model sera une table contenant la table model.args. La table model.args aura deux éléments : l'élément "stat" indexé par la chaîne de caractère "titre" et l'élément "ouvert" indexé par la chaîne de caractère "compte"
Si le modèle appelant possède un lien qui n’est pas renseigné, alors La table model.args retournera, à l'index correspondant, la chaîne vide "" (et pas nil).
À titre d'exemple, dans le Module:Frame nous écrivons la fonction p.Parent ainsi :
local p = {}
function p.parent(frame)
local reponse = ""
local model = frame:getParent()
for index, objet in pairs(model.args) do
reponse = reponse.."<br>À la clé "..index..", il y a l’objet : "..objet
end
return reponse
end
return p
Nous créons ensuite un Modèle:Parents dans lequel nous écrivons :
Les arguments du présent modèle sont :{{#invoke:Frame|parent}}
Nous donnerons deux exemples.
Premier exemple :
{{Parents|Grenouille|rouge|4}}
nous donne :
Les arguments du présent modèle sont :
À la clé 1, il y a l’objet : Grenouille
À la clé 2, il y a l’objet : rouge
À la clé 3, il y a l’objet : 4
Deuxième exemple :
{{Parents
| titre = Planing
| couleur = vert
| nombre = 63
}}
nous donne :
Les arguments du présent modèle sont :
À la clé nombre, il y a l’objet : 63
À la clé titre, il y a l’objet : Planing
À la clé couleur, il y a l’objet : vert
frame:newChild
modifierCette fonction permet d’utiliser des fonctions qui, normalement, attendent des arguments provenant d'une commande #invoke.
Dans l'exemple ci-dessous la fonction p.replique est rédigée comme si elle devait être utilisée grâce à l'appel {{#invoke:Frame|replique|jardin|maison}} par exemple. La fonction p.child fabrique un nouvel objet frame avec pour arguments "jeudi" et frame.args[2] et appelle la fonction p.replique sous la forme p.replique(newFrame), ce qui est alors équivalent à un appel du type {{#invoke:Frame|replique|jeudi|frame.args[2]}} que l’on n'aurait pas pu faire puisque nous ne pouvons pas utiliser la commande #invoke dans un module.
local p = {}
function p.replique(frame)
return "Je renvoie "..frame.args[1].." et "..frame.args[2]
end
function p.child(frame)
newFrame = frame:newChild{args={"Jeudi",frame.args[2]}}
return p.replique(newFrame)
end
return p
{{#invoke:Frame|child|3|Tulipe|7|janvier}} nous indique : Je renvoie Jeudi et Tulipe
frame:preprocess
modifierNous avons déjà eu l’occasion d'étudier cette fonction qui permet d'interpréter les modèles avant qu’ils ne soient retournés (voir le chapitre sur la Gestion de l'environnement).
Dans l'exemple ci-dessous, nous n'avons pas retourné directement ce que donnait la fonction frame:preprocess, mais nous avons commencé par stocker le retour dans la variable reponse et nous avons ensuite retourné la variable reponse. Ceci montre que ce que retourne la fonction frame:preprocess peut éventuellement être mémorisé dans une variable et subir un éventuel traitement dans le programme.
local p = {}
function p.process()
local reponse = ""
reponse = frame:preprocess("{{Attention|Essai avec le modèle Attention}}")
return "<br>La fonction a retourné : "..reponse
end
return p
{{#invoke:Frame|process}} nous indique :
La fonction a retourné :
Essai avec le modèle Attention |
frame:expandTemplate
modifierLa fonction "frame:expandTemplate" réalise à peu près la même chose que la fonction "frame:preprocess", mais uniquement pour les modèles. Par conséquent, pour les modèles, on préférera utiliser la fonction "frame:expandTemplate" car elle est plus rapide et moins sujette aux erreurs que la fonction "frame:preprocess".
Ci-dessous, nous donnons un exemple, réalisant la même chose, que celui donné pour la fonction "frame:preprocess".
local p = {}
function p.template(frame)
reponse = frame:expandTemplate({ title = "Attention", args = { "Essai avec le modèle Attention" } })
return reponse
end
return p
{{#invoke:Frame|template}} nous indique :
Essai avec le modèle Attention |
frame:argumentPairs
modifierL'écriture frame:argumentPairs() est strictement identique à l'écriture pairs( frame.args ) comme nous pouvons le constater dans l'exemple ci-dessous :
local p = {}
function p.pair(frame)
local reponse = ""
local nombre,chaine = 0,0
for index, objet in frame:argumentPairs() do
reponse = reponse.."<br>À la clé "..index..", il y a l’objet : "..objet
end
return reponse
end
return p
{{#invoke:Frame|pair|3|Tulipe|7|janvier}} nous indique :
À la clé 1, il y a l’objet : 3
À la clé 2, il y a l’objet : Tulipe
À la clé 3, il y a l’objet : 7
À la clé 4, il y a l’objet : janvier
En fait la fonction frame:argumentPairs n'existe que pour des raisons de compatibilité avec les versions antérieures de Scribunto.
Aujourd'hui, on préfèrera écrire pairs( frame.args ) à la place de frame:argumentPairs()
frame:callParserFunction
modifierCette fonction permet de faire appel à un des multiples parseurs (analyseurs) disponibles dans l'extension ParserFunctions. Ces fonctions permettent d'analyser des chaînes de caractères selon certains critères et de s'assurer qu'elles sont valides et utilisables.
local p = {}
function p.callParserFunction(frame)
return ';callParserFunction\n:' .. frame:callParserFunction('#time', 'Y-m-d H:i:s') .. '\n'
end
return p
{{#invoke:Frame|parser}} nous indique : La fonction a retourné : 2024-12-22 01:10:52 et son type est : string
frame:extensionTag
modifierCette fonction permet de poser un tag autour d'un texte. Un exemple type est le tag <nowiki> interdisant à l'interpréteur de page d'exécuter un contenu. Ainsi, si un contenu renvoyé par une fonction est susceptible d'être interprété comme un modèle, un lien ou autre, on peut utiliser la fonction frame:extensionTag() pour placer de manière automatique les balises <nowiki>.
Dans l’exemple suivant, on retourne le texte [[texte]] avec et sans l'utilisation de extensionTag.
local p = {}
function p.tag(frame)
local reponse = ""
reponse = frame:extensionTag('nowiki', '[[texte]]', {})
if reponse == nil then
return "Il y a un problème, la fonction a retourné : nil"
else
return "La fonction a retourné : "..reponse.." et son type est : "..type(reponse)
end
end
function p.notag(frame)
local reponse = ""
reponse = '[[texte]]'
if reponse == nil then
return "Il y a un problème, la fonction a retourné : nil"
else
return "La fonction a retourné : "..reponse.." et son type est : "..type(reponse)
end
end
return p
Rendu avec extensionTag : {{#invoke:Frame|tag}} nous indique : La fonction a retourné : [[texte]] et son type est : string
Rendu sans extensionTag : {{#invoke:Frame|notag}} nous indique : La fonction a retourné : texte et son type est : string
frame:getArgument
modifierCette fonction permet de récupérer un argument passé en paramètre au module. Il y a 2 signatures possibles :
- frame:getArgument(number) - où number représente l'index de l’argument à récupérer
- frame:getArgument(name) - où name représente le nom de la clé associée à l’argument à récupérer
La fonction retourne un objet offrant une méthode expand() permettant de récupérer la valeur interprétée. Cette valeur est obtenu en appelant : objet:expand() où objet est la variable dans laquelle a été stockée le résultat de l'exécution de la fonction.
Le type de l’objet étant une table, on peut en parcourir le contenu avec les fonctions pairs() ou ipairs(). La valeur obtenue sera alors brute, c'est-à-dire non interprétée.
local p = {}
function p.argument(frame)
local reponse = ""
reponse = frame:getArgument(1)
if reponse == nil then
return "Il y a un problème, la fonction a retourné : nil"
else
return "La fonction a retourné : "..reponse:expand().." et son type est : "..type(reponse)
end
end
function p.namedArgument(frame)
local reponse = ""
reponse = frame:getArgument("fleur")
if reponse == nil then
return "Il y a un problème, la fonction a retourné : nil"
else
return "La fonction a retourné : "..reponse:expand().." et son type est : "..type(reponse)
end
end
return p
{{#invoke:Frame|argument|3|fleur=Tulipe}} nous indique : La fonction a retourné : 3 et son type est : table
{{#invoke:Frame|namedArgument|3|fleur=Tulipe}} nous indique : La fonction a retourné : Tulipe et son type est : table
frame:newParserValue
modifierlocal p = {}
return p
{{#invoke:Frame|parval|3|Tulipe|7|janvier}} nous indique : La fonction a retourné une variable de type : table qui contient :
frame:newTemplateParserValue
modifierlocal p = {}
return p
{{#invoke:Frame|temparval|3|Tulipe|7|janvier}} nous indique : Erreur Lua dans mw.lua à la ligne 435 : frame:newTemplateParserValue: a title is required.
frame:getTitle
modifierCette fonction renvoie le nom du module Lua invoqué.
local p = {}
function p.title(frame)
local reponse = ""
reponse = frame:getTitle()
if reponse == nil then
return "Il y a un problème, la fonction a retourné : nil"
else
return "La fonction a retourné : "..reponse.." et son type est : "..type(reponse)
end
end
return p
{{#invoke:Frame|title}} nous indique : La fonction a retourné : Module:Frame et son type est : string
Liens Connexes
modifier
L'objet Title
Nous devons pas perdre de vue que le Lua a été implanté avec Scribunto afin de faciliter la gestion des pages se trouvant dans les différents projets de la fondation Wikimédia. Nous allons donc voir, dans ce chapitre, comment accéder à ces pages et plus précisément, comment accéder à divers renseignements concernant ces pages. Que ce soit le contenu des pages, le titre, ou d'autres renseignements, nous allons voir que nous disposons de toute une palette de fonctions utiles et pratiques qu’il nous faut bien maîtriser.
L'objet Title
modifierLa librairie Title
modifierDans ce paragraphe, nous allons étudier en détail les fonctions se trouvant dans la librairie Title.
mw.title.new
modifierCette commande est essentielle car c’est elle qui va créer un nouvel objet Title concernant la page passée en argument. Cette commande renvoie une table contenant tout ce qui est nécessaire pour obtenir des renseignements concernant la page rentrée en argument. Par exemple :
après :
local title1 = mw.title.new(page1)
local title2 = mw.title.new(page2)
Les tables title1 et title2 contiendront tout ce qui est nécessaire pour obtenir des renseignements respectivement sur les pages dont les noms sont contenus dans les variables page1 et page2.
Comme exemple d'application, voir tous les exemples donnés ci-dessous car, sans cette commande, aucun accès aux pages n'est possible.
Nous devons aussi notez que cette commande est très vorace en temps d'exécution. En conséquence, il n’est pas autorisé de l’utiliser plus de 99 fois dans une même page. Voir : mw:Manual:$wgExpensiveParserFunctionLimit |
Contrairement à ce que l’on pourrait croire à priori, si la page entrée en argument n'existe pas, un objet title est tout de même créé. C'est à l'intérieur de cet objet title que l’on aura des indications sur l'existence ou non de la page |
mw.title.equals
modifiermw.title.compare
modifiermw.title.getCurrentTitle
modifierCette fonction crée un objet Title comme le ferait la fonction title.new mais concernant la page ou se trouve le module contenant la fonction. Si le module contenant cette fonction se trouve dans un modèle, la fonction retournera le titre de la page dans laquelle le modèle est placé.
Pour un exemple d'utilisation, voir le sous- paragraphe title.text plus bas, dans cette page.
mw.title.makeTitle
modifierNous avons ici une troisième fonction qui créé un objet Title. Sa particularité est que le titre de la page, dont on veut créer l’objet Title, est fourni par morceaux à travers ses paramètres.
mw.title.makeTitle(Espace,titre,fragment, interwiki)
Pour le paramètre espace, on rentrera le numéro ou le nom de l'espace où se trouve la page.
Pour la Wikiversité, nous donnons la correspondance entre le numéro d'espace et le nom d'espace dans le tableau suivant :
|
Pour le paramètre titre, on rentrera le titre de la page, sans préfixe.
Pour le paramètre fragment, on rentrera le fragment. Qu’est ce qu'un fragment ? voir à le propos la page Identificateur de fragment
Pour le paramètre interwiki, voir la page Interwiki
Les deux derniers paramètres fragment et interwiki sont facultatifs (ouf!)
Question : Pourquoi s’embêter avec la fonction mw.title.makeTitle ? Pourquoi ne pas prendre la fonction mw.title.new et taper simplement en paramètre le nom de la page avec son préfixe ? Réponse : Pour des besoins de programmation, les paramètres de la fonction mw.title.makeTitle peuvent être variables et sont fournis par d'autres fonctions ou des variables. En fait la fonction mw.title.makeTitle se trouvera dans un module placé dans un modèle qui sera placé dans un grand nombre de pages et l’objet Title que l’on voudra obtenir dépendra de la page où est placé le modèle. |
Nous donnons ci-dessous un exemple simple :
local p = {}
function p.creation(frame)
local reponse = ""
local title = mw.title.makeTitle(frame.args[1],frame.args[2])
reponse = reponse.."<br>Le titre de la page dont on vient de créer l’objet Title est : "..title.prefixedText
return reponse
end
return p
(Voir title.prefixedText que nous avons utilisé dans le programme)
{{#invoke:Title|creation|12|Maintenance à l'aide du Lua}} nous donne :
Le titre de la page dont on vient de créer l’objet Title est : Aide:Maintenance à l'aide du Lua
Objets en relation avec l’objet Title
modifierTous les exemples de ce paragraphe se trouvent dans le Module:Title
Les objets que nous allons étudier dans ce paragraphe se trouvent dans l’objet Title. Nous commencerons donc par écrire une fonction p.visualisation permettant de les visualiser.
local p = {}
function p.visualisation(frame)
title = mw.title.new(frame.args[1])
reponse = ""
for index, objet in pairs(title) do
reponse = reponse.."<br />À la clé '''"..index.."''', on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Title|visualisation|{{PAGENAME}}}} nous donne :
À la clé isRedirect, on trouve un objet de type : boolean
À la clé fullUrl, on trouve un objet de type : function
À la clé canonicalUrl, on trouve un objet de type : function
À la clé fragment, on trouve un objet de type : string
À la clé subjectPageTitle, on trouve un objet de type : table
À la clé nsText, on trouve un objet de type : string
À la clé thePartialUrl, on trouve un objet de type : string
À la clé text, on trouve un objet de type : string
À la clé prefixedText, on trouve un objet de type : string
À la clé isTalkPage, on trouve un objet de type : boolean
À la clé exists, on trouve un objet de type : boolean
À la clé talkPageTitle, on trouve un objet de type : table
À la clé inNamespace, on trouve un objet de type : function
À la clé isSubpageOf, on trouve un objet de type : function
À la clé canTalk, on trouve un objet de type : boolean
À la clé rootText, on trouve un objet de type : string
À la clé hasSubjectNamespace, on trouve un objet de type : function
À la clé partialUrl, on trouve un objet de type : function
À la clé isSubpage, on trouve un objet de type : boolean
À la clé isLocal, on trouve un objet de type : boolean
À la clé talkNsText, on trouve un objet de type : string
À la clé basePageTitle, on trouve un objet de type : table
À la clé baseText, on trouve un objet de type : string
À la clé interwiki, on trouve un objet de type : string
À la clé isExternal, on trouve un objet de type : boolean
À la clé subjectNsText, on trouve un objet de type : string
À la clé isSpecialPage, on trouve un objet de type : boolean
À la clé subPageTitle, on trouve un objet de type : function
À la clé inNamespaces, on trouve un objet de type : function
À la clé id, on trouve un objet de type : number
À la clé subpageText, on trouve un objet de type : string
À la clé localUrl, on trouve un objet de type : function
À la clé redirectTarget, on trouve un objet de type : boolean
À la clé isContentPage, on trouve un objet de type : boolean
À la clé fullText, on trouve un objet de type : string
À la clé cascadingProtection, on trouve un objet de type : table
À la clé rootPageTitle, on trouve un objet de type : table
À la clé contentModel, on trouve un objet de type : string
À la clé protectionLevels, on trouve un objet de type : table
À la clé namespace, on trouve un objet de type : number
À la clé getContent, on trouve un objet de type : function
Nous voyons que nous avons un grand nombre d'objets à étudier, alors ne perdons pas de temps !
title.Redirect
modifiertitle.Redirect indique si la page est une redirection.
Exemple :
local p = {}
function p.redirection(frame)
local page = frame.args[1]
local title = mw.title.new(page)
if title.Redirect then
return "<br />La page '''"..page.."''' est une redirection."
else
return "<br />La page '''"..page.."''' n’est pas une redirection."
end
end
return p
{{#invoke:Title|redirection|Bac à sable}} nous donne :
La page Bac à sable n’est pas une redirection.
{{#invoke:Title|redirection|Aide:Maintenance à l'aide du Lua}} nous donne :
La page Aide:Maintenance à l'aide du Lua n’est pas une redirection.
(Et l’on constate que cela ne marche pas ! un bon point pour celui qui trouvera pourquoi ! )
title.fullUrl
modifier
title.fullUrl est de type function. title.fullUrl(query, proto) Retourne l'URL complète de la page (avec optionnellement un "query", une chaîne ou une table). proto est optionnel et permet de préciser "http", "https", "relative" (par défaut), ou "canonical".
title.canonicalUrl
modifier
title.canonicalUrl est de type function. title.canonicalUrl(query) retourne l'URL canonique de la page (avec optionnellement un "query", une chaîne ou une table)
title.fragment
modifier
title.fragment est de type string et donne le fragment (voir w:Identificateur de fragment).
title.subjectPageTitle
modifier
identique à mw.title.makeTitle( mw.site.namespaces[title.namespace].subject.id, title.text ).
title.isExternal
modifier
indique si la page a un interwiki
title.thePartialUrl
modifier
title.text
modifiertitle.text contient le titre de la page concernée par l’objet Title. On imagine facilement qu’il serait absurde de créer un objet Title en utilisant la fonction mw.title.new pour avoir le titre d'une page car justement le titre de la page est fourni en argument à la fonction mw.title.new. L'objet Title sera donc, dans ce cas, logiquement fourni par la fonction mw.title.getCurrentTitle ou par la fonction mw.title.makeTitle.
Écrivons, par exemple, une fonction p.titre qui nous indique le titre de la page que nous sommes en train de lire !
local p = {}
function p.titre()
local titr = mw.title.getCurrentTitle()
return "Le titre de cette page est : "..titr.text
end
return p
{{#invoke:Title|titre}} nous donne : Le titre de cette page est : Initiation au Lua avec Scribunto/Version imprimable
title.prefixedText
modifiertitle.prefixedText est de type string. Cette variable contient le titre de la page avec l'espace de noms ainsi que l'interwiki.
Si l’on souhaite avoir en plus le fragment, on utilisera à la place title.fullText
Donnons un exemple :
Le programme, ci-dessous, créé un objet Title et affiche le nom complet de la page concernée
local p = {}
function p.creation(frame)
local reponse = ""
local title = mw.title.makeTitle(frame.args[1],frame.args[2])
reponse = reponse.."<br>Le titre de la page dont on vient de créer l’objet Title est : "..title.prefixedText
return reponse
end
return p
{{#invoke:Title|creation|Aide|Maintenance à l'aide du Lua}} nous donne :
Le titre de la page dont on vient de créer l’objet Title est : Aide:Maintenance à l'aide du Lua
title.isTalkPage
modifierindique si la page est une page de discussion
Exemple :
local p = {}
function p.discussion(frame)
local page = frame.args[1]
local title = mw.title.new(page)
if title.isTalkPage then
return "<br />La page '''"..page.."''' est une page de discussion."
else
return "<br />La page '''"..page.."''' n’est pas une page de discussion."
end
end
return p
{{#invoke:Title|discussion|Discussion:Initiation au Lua avec Scribunto}} nous donne :
La page Discussion:Initiation au Lua avec Scribunto est une page de discussion.
{{#invoke:Title|discussion|Aide:Maintenance à l'aide du Lua}} nous donne :
La page Aide:Maintenance à l'aide du Lua n’est pas une page de discussion.
title.exists
modifierC'est un booléen qui nous indique si la page existe. Ceci peut paraître bizarre dans la mesure où l’on pourrait croire que, si une page n'existe pas, la fonction mw.title.new() devrait nous retourner nil. C'est faux ! Si la page n'existe pas alors l’objet title est tout de même créé, mais le booleen title.exists sera à false. Nous allons vérifier ce que l’on vient de dire en faisant un programme qui nous indique si, d'une part, l’objet title existe et si, d’autre part, la page associée à cet objet title existe.
local p = {}
function p.existe(frame)
local reponse = ""
local page = frame.args[1]
local title = mw.title.new(page)
if title then
reponse = reponse.."L'objet title existe "
else
reponse = reponse.."L'objet title n'existe pas "
end
if title.exists then
reponse = reponse.."et la page existe. "
else
reponse = reponse.."et la page n'existe pas. "
end
return reponse
end
return p
{{#invoke:Title|existe|Bof}} nous donne : L'objet title existe et la page n'existe pas.
{{#invoke:Title|existe|Aide:Maintenance à l'aide du Lua}} nous donne : L'objet title existe et la page existe.
title.talkPageTitle
modifierCette commande est identique à mw.title.makeTitle( mw.site.namespaces[title.namespace].talk.id, title.text ), ou à nil si la page ne peut pas avoir de page de discussion.
Plus simplement, cette commande créé un objet title pour la page de discussion associée à la page dont on a créé un objet title appelé title
Par exemple si, sur la Wikiversité, on a créé un objet title pour une page de l'espace principal appelé title, cette commande est identique a mw.title.makeTitle( 1 , title.text ). Car 1 est le numéro d'espace pour une page de discussion de l'espace principal.
title.inNamespace
modifier
title.inNamespace est de type function. title.inNamespace(ns) indique si la page est dans l'espace de noms indiqué. ns peut être toute clé trouvée dans mw.site.namespaces.
title.isSubpageOf
modifier
title.isSubpageOf est de type function. title.isSubpageOf(title2) indique si la page est une sous-page de title2.
title.canTalk
modifierindique si la page peut avoir une page de discussion
Exemple :
local p = {}
function p.discute(frame)
local page = frame.args[1]
local title = mw.title.new(page)
if title.canTalk then
return "<br />La page '''"..page.."''' peut avoir une page de discussion."
else
return "<br />La page '''"..page.."''' ne peut pas avoir une page de discussion."
end
end
return p
{{#invoke:Title|discute|Spécial:Nouvelles pages}} nous donne :
La page Spécial:Nouvelles pages ne peut pas avoir une page de discussion.
{{#invoke:Title|discute|Aide:Maintenance à l'aide du Lua}} nous donne :
La page Aide:Maintenance à l'aide du Lua peut avoir une page de discussion.
title.getContent
modifierCette commande retourne le contenu de la page concernée par l’objet Title. Contrairement à ce que l’on pourrait penser a priori, l'argument de cette commande est l’objet Title retourné par la commande mw.title.new et pas le nom de la page dont on extrait le contenu. Par exemple, on écrira :
local title = mw.title.new(page)
local texte = title.getContent(title)
C'est-à-dire que l’on commence par créer un objet Title (ici la table title) et on passe cette table comme argument de la fonction title.getContent pour recueillir dans la variable texte le contenu de la page dont le titre était dans la variable page.
Question : Pourquoi n'existe-t-il pas une fonction qui permettrait d'obtenir directement le contenu d'une page en donnant le nom de cette page en argument ? Ce qui éviterait la création d'un objet Title et devrait permettre ainsi de gagner en temps d'exécution ! Réponse : (Si vous avez la réponse à cette question, veuillez remplacer cette phrase entre parenthèse par celle-ci) |
Donnons un exemple concret en écrivant une fonction p.mot qui permet de dire si un mot se trouve dans la page : Statique/Présentation de la leçon.
local p = {}
function p.mot(frame)
local mot = frame.args[1]
local page = "Statique/Présentation de la leçon"
local title = mw.title.new(page)
local texte = title.getContent(title)
local position = mw.ustring.find(texte,mot)
if position then
return "Le mot "..mot.." a été trouvé en position "..position
else
return "Le mot "..mot.." n'a pas été trouvé dans la page"
end
end
return p
{{#invoke:Title|mot|système}} nous donne : Le mot système a été trouvé en position 108
{{#invoke:Title|mot|cacahuète}} nous donne : Le mot cacahuète n'a pas été trouvé dans la page
title.hasSubjectNamespace
modifierhasSubjectNamespace est de type function. hasSubjectNamespace(ns) indique si l'espace de noms sujet de la page est dans l'espace de noms indiqué. ns peut être toute clé trouvée dans mw.site.namespaces
title.partialUrl
modifier
title.partialUrl est de type function. title.partialUrl(text) retourne text encodé comme une URL
title.isSubpage
modifierIndique si la page est une sous-page.
Exemple :
local p = {}
function p.souspage(frame)
local page = frame.args[1]
local title = mw.title.new(page)
if title.isSubpage then
return "<br />La page '''"..page.."''' est une sous-page."
else
return "<br />La page '''"..page.."''' n’est pas une sous-page."
end
end
return p
{{#invoke:Title|souspage|Initiation au Lua avec Scribunto/L'objet Frame}} nous donne :
La page Initiation au Lua avec Scribunto/L'objet Frame est une sous-page.
{{#invoke:Title|souspage|Aide:Maintenance à l'aide du Lua}} nous donne :
La page Aide:Maintenance à l'aide du Lua n’est pas une sous-page.
title.isSpecialPage
modifierIndique si la page est dans l'espace de noms "Spécial:"
Exemple :
local p = {}
function p.special(frame)
local page = frame.args[1]
local title = mw.title.new(page)
if title.isSpecialPage then
return "<br />La page '''"..page.."''' est une page spéciale."
else
return "<br />La page '''"..page.."''' n’est pas une page spéciale."
end
end
return p
{{#invoke:Title|special|Spécial:Nouvelles pages}} nous donne :
La page Spécial:Nouvelles pages est une page spéciale.
{{#invoke:Title|special|Aide:Maintenance à l'aide du Lua}} nous donne :
La page Aide:Maintenance à l'aide du Lua n’est pas une page spéciale.
title.basePageTitle
modifiertitle.basePageTitle est une fonction qui retourne un nouvel objet title. Si title est un objet title concernant une sous-page, alors title.basePageTitle retourne un objet title concernant la page ayant pour sous-page, la sous-page du premier objet title.
Plus précisément, si title est par exemple l’objet title de la page : Recherche:Les asticots/Introduction, title.basePageTitle retournera un objet title associé à la page : Recherche:Les asticots
Bien sur, si le premier objet title concerne une page qui n’est pas une sous-page, le deuxième objet title, créé par title.basePageTitle, concernera la même page
Écrivons un petit programme pour mieux comprendre la syntaxe et voir l'effet sur différents types de pages :
local p = {}
function p.basepagetitle(frame)
local reponse = ""
local page = frame.args[1]
local title = mw.title.new(page)
local titlebase = title.basePageTitle
reponse = reponse.."<br>Le premier objet title concerne la page : "..title.prefixedText
reponse = reponse.."<br>Le deuxième objet title concerne la page : "..titlebase.prefixedText
return reponse
end
return p
{{#invoke:Title|basepagetitle|Bof/Niouf}} nous donne :
Le premier objet title concerne la page : Bof/Niouf
Le deuxième objet title concerne la page : Bof (bien que la page Bof/Niouf n'existe pas!)
{{#invoke:Title|basepagetitle|Aide:Maintenance à l'aide du Lua}} nous donne :
Le premier objet title concerne la page : Aide:Maintenance à l'aide du Lua
Le deuxième objet title concerne la page : Aide:Maintenance à l'aide du Lua
{{#invoke:Title|basepagetitle|Recherche:Les fonds patrimoniaux des bibliothèques publiques/Parchemin et cuir}} nous donne :
Le premier objet title concerne la page : Recherche:Les fonds patrimoniaux des bibliothèques publiques/Parchemin et cuir
Le deuxième objet title concerne la page : Recherche:Les fonds patrimoniaux des bibliothèques publiques
Nous voyons aussi que title.basePageTitle est identique à mw.title.makeTitle(title.namespace,title.baseText)
title.baseText
modifiertitle.baseText est un objet de type string. Si l’on a affaire à une sous-page, title.baseText donne le titre de la page dont c’est la sous-page, sans préfixe d'espace. Sinon title.baseText a le même effet que title.text. Nous voyons alors que title.baseText est l'équivalent de {{BASEPAGENAME}} dans la syntaxe wiki. Écrivons un petit programme pour voir comment se comporte title.baseText avec un choix de page donnée en argument :
local p = {}
function p.base(frame)
local reponse = ""
local page = frame.args[1]
local title = mw.title.new(page)
reponse = reponse.."Pour la page : '''"..page.."''', ''title.baseText'' nous donne : '''"..title.baseText.."'''."
return reponse
end
return p
{{#invoke:Title|base|Bof/Niouf}} nous donne : Pour la page : Bof/Niouf, title.baseText nous donne : Bof. (bien que la page Bof/Niouf n'existe pas!)
{{#invoke:Title|base|Aide:Maintenance à l'aide du Lua}} nous donne : Pour la page : Aide:Maintenance à l'aide du Lua, title.baseText nous donne : Maintenance à l'aide du Lua.
{{#invoke:Title|base|Recherche:Les fonds patrimoniaux des bibliothèques publiques/Parchemin et cuir}} nous donne : Pour la page : Recherche:Les fonds patrimoniaux des bibliothèques publiques/Parchemin et cuir, title.baseText nous donne : Les fonds patrimoniaux des bibliothèques publiques.
title.nsText
modifier
title.nsText est de type string et donne le nom de l'espace de noms de la page concernée.
title.isLocal
modifierIndique si la page est locale au projet. Par exemple sur en: tous les Wikipédias sont considérés comme locaux mais pas les Wiktionnaires.
Exemple :
local p = {}
function p.locale(frame)
local page = frame.args[1]
local title = mw.title.new(page)
if title.isLocal then
return "<br />La page '''"..page.."''' est locale au projet."
else
return "<br />La page '''"..page.."''' n’est pas locale au projet."
end
end
return p
{{#invoke:Title|locale|Initiation au Lua avec Scribunto}} nous donne :
La page Initiation au Lua avec Scribunto est locale au projet.
{{#invoke:Title|locale|en:wikt:amandantium}} nous donne :
La page en:wikt:amandantium est locale au projet.
(ça n'a pas l'air de marcher, à revoir)
title.fullText
modifier
title.fullText est de type string et donne le titre de la page, avec l'espace de nom, l'interwiki et le fragment.
title.subPageTitle
modifier
title.subPageTitle est de type function. subPageTitle(text) est identique à mw.title.makeTitle( title.namespace, title.text .. '/' .. text )
title.inNamespaces
modifier
title.inNamespaces est de type function. title.inNamespaces(...) indique si la page est dans l'un des espaces de nom indiqués. Les espaces de noms peuvent être toute clé trouvée dans mw.site.namespaces.
title.id
modifierC'est un objet de type number qui nous donne l'identifiant de la page. Chaque page créée sur un Wiki a un identifiant. La première page créée a l'identifiant 1. La deuxième page créée a l'identifiant 2 et ainsi de suite. Si la page n'existe pas, ce nombre sera égal à 0. Nous allons vérifier ce que l’on vient de dire en faisant un programme qui nous indique si la page entrée en argument existe et quel est son identifiant.
local p = {}
function p.identifiant(frame)
local reponse = ""
local page = frame.args[1]
local title = mw.title.new(page)
if title.exists then
reponse = reponse.."La page existe. "
else
reponse = reponse.."La page n'existe pas. "
end
reponse = reponse.."L'identifiant de la page est "..title.id
return reponse
end
return p
{{#invoke:Title|identifiant|Bof}} nous donne : La page n'existe pas. L'identifiant de la page est 0
{{#invoke:Title|identifiant|Aide:Maintenance à l'aide du Lua}} nous donne : La page existe. L'identifiant de la page est 51770
title.subpageText
modifiertitle.subpageText est un objet de type string. Si l’on a affaire à une sous-page, title.subpageText donne le suffixe de la page. Sinon title.subpageText a le même effet que title.text. Nous voyons alors que title.subpageText est l'équivalent de {{SUBPAGENAME}} dans la syntaxe wiki. Écrivons un petit programme pour voir comment se comporte title.subpageText avec un choix de page donnée en argument :
local p = {}
function p.subpage(frame)
local reponse = ""
local page = frame.args[1]
local title = mw.title.new(page)
reponse = reponse.."Pour la page : '''"..page.."''', ''title.subpageText'' nous donne : '''"..title.subpageText.."'''."
return reponse
end
return p
{{#invoke:Title|subpage|Bof/Niouf}} nous donne : Pour la page : Bof/Niouf, title.subpageText nous donne : Niouf. (bien que la page Bof/Niouf n'existe pas!)
{{#invoke:Title|subpage|Aide:Maintenance à l'aide du Lua}} nous donne : Pour la page : Aide:Maintenance à l'aide du Lua, title.subpageText nous donne : Maintenance à l'aide du Lua.
{{#invoke:Title|subpage|Recherche:Les fonds patrimoniaux des bibliothèques publiques/Parchemin et cuir}} nous donne : Pour la page : Recherche:Les fonds patrimoniaux des bibliothèques publiques/Parchemin et cuir, title.subpageText nous donne : Parchemin et cuir.
title.subjectNsText
modifier
title.subjectNsText est de type string et donne le nom de l'espace de noms sujet de la page.
title.rootText
modifier
title.rootText est de type string et donne le titre de la page de base sans préfixe si l’on se trouve dans une sous-page. Sinon est identique à title.text.
indique si la page est une sous-page
title.isContentPage
modifierIndique si la page est dans un espace de noms de contenu.
Exemple :
local p = {}
function p.contenu(frame)
local page = frame.args[1]
local title = mw.title.new(page)
if title.isContentPage then
return "<br />La page '''"..page.."''' est une page de contenu."
else
return "<br />La page '''"..page.."''' n’est pas une page de contenu."
end
end
return p
{{#invoke:Title|contenu|Initiation au Lua avec Scribunto}} nous donne :
La page Initiation au Lua avec Scribunto est une page de contenu.
{{#invoke:Title|contenu|Bac à sable}} nous donne :
La page Bac à sable est une page de contenu.
{{#invoke:Title|contenu|Recherche:Évolution prébiotique}} nous donne :
La page Recherche:Évolution prébiotique est une page de contenu.
{{#invoke:Title|contenu|Faculté:Informatique}} nous donne :
La page Faculté:Informatique n’est pas une page de contenu.
{{#invoke:Title|contenu|Aide:Maintenance à l'aide du Lua}} nous donne :
La page Aide:Maintenance à l'aide du Lua n’est pas une page de contenu.
Il semblerait que seules les pages de l'espace principal, y compris les redirections, soient des pages de contenu.
title.cascadingProtection
modifier
title.rootPageTitle
modifier
identique à mw.title.makeTitle( title.namespace, title.rootText )
title.localUrl
modifier
title.localUrl est de type function. title.localUrl(query) retourne l'URL locale de la page (avec optionnellement un "query", une chaîne ou une table)
title.contentModel
modifier
Le type de contenu pour ce titre, sous forme de chaîne. Voir mw:Manual:ContentHandler.
title.protectionLevels
modifier
title.interwiki
modifier
title.interwiki est de type string et donne le préfixe interwiki. S'il n'y a pas de préfixe interwiki, la chaîne de caractère retournée est vide.
title.namespace
modifiertitle.namespace est de type number et permet d'obtenir le numéro d'espace dans lequel se trouve la page consernée.
local p = {}
function p.num_espace(frame)
local page = frame.args[1]
local title = mw.title.new(frame.args[1])
return "<br />La page '''"..page.."''' se trouve dans l'espace de numéro : ".. title.namespace
end
return p
{{#invoke:Title|num_espace|Aide:Maintenance à l'aide du Lua}} nous donne :
La page Aide:Maintenance à l'aide du Lua se trouve dans l'espace de numéro : 12
L'objet URI
L'objet URI
modifierLibrairie Uri
modifierNous commencerons par visualiser le contenu de cette librairie grâce au programme suivant :
local p = {}
function p.visualisation(frame)
reponse = ""
for index, objet in pairs(mw.uri) do
reponse = reponse.."<br>À la clé "..index..", on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Uri|visualisation}} nous donne :
À la clé parseQueryString, on trouve un objet de type : function
À la clé encode, on trouve un objet de type : function
À la clé fullUrl, on trouve un objet de type : function
À la clé canonicalUrl, on trouve un objet de type : function
À la clé buildQueryString, on trouve un objet de type : function
À la clé anchorEncode, on trouve un objet de type : function
À la clé validate, on trouve un objet de type : function
À la clé localUrl, on trouve un objet de type : function
À la clé decode, on trouve un objet de type : function
À la clé new, on trouve un objet de type : function
Étudions ces fonctions dans l’ordre où elles sont sorties :
mw.uri.parseQueryString
modifiermw.uri.parseQueryString( s )
Décode une chaine correspondant à une requête en une table. Les clés qui dans la chaîne n'ont aucune value prendront la valeur false ; les clés répétées de multiples fois auront pour valeur des séquences ; les autres auront pour valeur des chaînes.
mw.uri.encode
modifiermw.uri.encode( s, enctype )
Encode la chaîne s au format URL. Le type par défaut "QUERY" encode les espaces en utilisant "+", "PATH" les encode en utilisant "%20" et "WIKI" les encode en utilisant "_".
Notez que le format "WIKI" n’est pas entièrement réversible puisque les espaces et les underscores ("_") sont encodés de la même façon.
mw.uri.fullUrl
modifiermw.uri.fullUrl( page, query )
Retourne un objet URI pour l'URL complet de cette page, avec une requête optionnelle string/table.
mw.uri.canonicalUrl
modifiermw.uri.canonicalUrl( page, query )
Retourne un objet URI for the canonical url for a page, with optional query string/table.
mw.uri.buildQueryString
modifiermw.uri.buildQueryString( table )
Encode une table en tant que chaîne de requête. Les clés doivent être des chaînes. Les valeurs peuvent être des chaînes, des nombres, des séquences ou false.
mw.uri.anchorEncode
modifiermw.uri.anchorEncode( s )
Encode la chaîne pour être utilisée en tant que fragment MediaWiki.
mw.uri.validate
modifiermw.uri.validate( table )
Validates the passed table (or URI object). Returns a boolean indicating whether the table was valid, and on failure a string explaining what problems were found.
mw.uri.localUrl
modifiermw.uri.localUrl( page, query )
Retourne un objet URI for the local url for a page, with optional query string/table.
mw.uri.decode
modifiermw.uri.decode( s, enctype )
Décode la chaîne "s". Le type par défaut "QUERY" décode les "+" en espaces, "PATH" décode les "%20" en espaces et "WIKI" décode les "_" en espaces.
mw.uri.new
modifiermw.uri.new( s )
Construit un nouvel objet URI pour la chaîne ou la table passée en argument. Voir la description de l’objet URI pour les champs possibles de la table.
Objets en relation avec l’objet URI
modifierNous commencerons par visualiser le contenu d'un objet URI grâce au programme suivant :
local p = {}
function p.visualise(frame)
reponse = ""
Uri = mw.uri.new(essai)
for index, objet in pairs(Uri) do
reponse = reponse.."<br>À la clé "..index..", on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Uri|visualise}} nous donne :
À la clé path, on trouve un objet de type : string
À la clé hostPort, on trouve un objet de type : string
À la clé host, on trouve un objet de type : string
À la clé validate, on trouve un objet de type : function
À la clé authority, on trouve un objet de type : string
À la clé clone, on trouve un objet de type : function
À la clé extend, on trouve un objet de type : function
À la clé parse, on trouve un objet de type : function
À la clé relativePath, on trouve un objet de type : string
Étudions ces fonctions dans l’ordre où elles sont sorties :
L'objet URI contient les champs suivants, dont certain ou tous peuvent valoir nil :
- protocol : chaîne du protocole
- user : nom de l'utilisateur
- password : mot de passe
- host : nom de la machine
- port : numéro du port (entier)
- path : chemin
- query : une table comme dans mw.uri.parseQueryString
- fragment : le fragment
Les propriétés suivantes sont également disponibles :
- userInfo : utilisateur et mot de passe
- hostPort : machine et numéro de port
- authority : les deux précédents ensembles
- queryString : version chaîne de la table de requête
- relativePath : chemin, requête et fragment
tostring()
retourne la chaîne de l'URI.
Les méthodes de l’objet URI sont :
mw.uri:parse
modifieruri:parse( s )
Analyse une chaîne dans l’objet URI courant. Tout champ spécifié dans la chaîne sera remplacé dans l'objet, les champs non spécifiés gardent leur ancienne valeur.
mw.uri:clone
modifieruri:clone()
Crée une copie de l’objet URI.
mw.uri:extend
modifieruri:extend( parameters )
Intègre la table parameters dans la table de requête de l'objet.
Librairie HTML
Présentation
modifiermw.html
est une interface fluide pour construire du code HTML complexe pour Lua. Le code est ainsi à la fois plus concis et plus clair. Un objet mw.html peut être créé en utilisant mw.html.create
.
Un exemple de base pourrait ressembler à ceci :
local div = mw.html.create( 'div' )
div
:attr( 'id', 'testdiv' )
:css( 'width', '100%' )
:wikitext( 'Du texte' )
:tag( 'hr' )
return tostring( div )
-- Sortie : <div id="testdiv" style="width:100%;">Du texte<hr /></div>
Fonctions de la librairie HTML
modifierLes exemples de ce chapitre se trouveront dans le Module:Html
Nous commencerons par visualiser le contenu de la librairie HTML grâce au programme suivant :
local p = {}
function p.visualisation(frame)
reponse = ""
for index, objet in pairs(mw.html) do
reponse = reponse.."<br>À la clé "..index..", on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Html|visualisation}} nous donne :
À la clé create, on trouve un objet de type : function
Nous voyons qu’il y a une seule fonction directement accessible, mais il y a aussi plusieurs méthodes.
Étudions en détail cette fonctions :
mw.html.create
modifiermw.html.create( tagName, args )
Crée un nouvel objet mw.html contenant un élément html tagName
. Vous pouvez également passer une chaîne vide au paramètre tagName
afin de créer un objet mw.html vide.
args
peut être une table avec les clés suivantes :
args.selfClosing
: Forcer la balise courante à se fermer, même si mw.html ne la reconnaît pas comme autofermanteargs.parent
: Parent de l'instance de mw.html actuel (destiné à un usage interne)
Méthodes de la librairie HTML
modifiermw.html:node
modifierhtml:node( builder )
Ajoute un nœud enfant (builder
) à l'instance mw.html actuelle.
mw.html:wikitext
modifierhtml:wikitext( ... )
Ajoute un nombre indéterminé de wikitexte à l’objet mw.html.
mw.html:newline
modifierhtml:newline()
Ajoute une nouvelle ligne à l’objet mw.html.
mw.html:tag
modifierhtml:tag( tagName, args )
Ajoute un nouveau nœud enfant tagName
, et renvoie une instance mw.html représentant le nouveau nœud. Le paramètre args
est identique à celui de mw.html.create
mw.html:attr
modifierhtml:attr( name, value )
html:attr( table )
Définie un attribut HTML name
avec comme valeur value
sur le noeud.
Alternativement, une table contenant le couple d'attribut nom->valeur peut être passée.
mw.html:getAttr
modifierhtml:getAttr( name )
Obtiens la valeur d'un attribut HTML précédemment définie en utilisant html:attr()
avec le nom (name
) donné.
mw.html:addClass
modifierhtml:addClass( class )
Ajoute un nom de classe à l'attribut de classe du noeud.
mw.html:css
modifierhtml:css( name, value )
html:css( table )
Définie une propriété CSS name
avec comme valeur value
sur le noeud.
Alternativement, une table contenant le couple de propriété nom->valeur peut être passée.
mw.html:cssText
modifierhtml:cssText( css )
Ajoute du code CSS brut à l'attribut du nœud courant.
mw.html:done
modifierhtml:done()
Renvoie le noeud parent dans lequel le nœud courant a été créé. Comme jQuery.end, il s'agit d'une fonction pratique pour permettre la construction de plusieurs nœuds enfants pour être chainés en une seule déclaration.
mw.html:allDone
modifierhtml:allDone()
Comme html:done()
, mais traverse tout le chemin vers le noeud racine de l'arbre et le retourne.
Étude du contenu d'un objet mw.html
modifierNous commencerons par visualiser le contenu d'un objet mw.html grâce au programme suivant :
local p = {}
function p.visualise(frame)
reponse = ""
html = mw.html.create(essai)
for index, objet in pairs(html) do
reponse = reponse.."<br>À la clé "..index..", on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Html|visualise}} nous donne :
À la clé nodes, on trouve un objet de type : table
À la clé styles, on trouve un objet de type : table
À la clé attributes, on trouve un objet de type : table
À la clé selfClosing, on trouve un objet de type : boolean
Étudions ces objets :
html.nodes
modifier
html.styles
modifier
html.attributes
modifier
html.selfClosing
modifier
Librairie Language
Présentation
modifierLes codes des langues sont décrits dans Language code. Bon nombre des codes de langue de MediaWiki sont similaires aux codes de langue IETF, mais tous les codes de langue de MediaWiki ne sont pas des codes langue IETF valides ou vice-versa.
Les fonctions documentées comme mw.language.name
sont disponibles dans la table globale mw.language
. Les fonctions documentées comme mw.language:name
sont des méthodes d'un objet Language (voir mw.language.new
).
Fonctions de la librairie language
modifierLes exemples de ce chapitre se trouveront dans le Module:Language
Nous commencerons par visualiser le contenu de la librairie language grâce au programme suivant :
local p = {}
function p.visualisation(frame)
reponse = ""
for index, objet in pairs(mw.langage) do
reponse = reponse.."<br>À la clé "..index..", on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Language|visualisation}} nous donne :
À la clé isValidCode, on trouve un objet de type : function
À la clé getFallbacksFor, on trouve un objet de type : function
À la clé isSupportedLanguage, on trouve un objet de type : function
À la clé new, on trouve un objet de type : function
À la clé fetchLanguageNames, on trouve un objet de type : function
À la clé isValidBuiltInCode, on trouve un objet de type : function
À la clé fetchLanguageName, on trouve un objet de type : function
À la clé isKnownLanguageTag, on trouve un objet de type : function
À la clé getContentLanguage, on trouve un objet de type : function
Nous voyons que nous avons 9 fonctions.
Étudions ces fonctions dans l’ordre où elles sont sorties :
mw.language.isValidCode
modifiermw.language.isValidCode( code )
Retourne vrai si une chaîne de code langue est d'une forme valide, qu’il existe ou pas. Ceci inclut les codes qui sont utilisés pour la personnalisation par les espaces de noms MediaWiki.
Le code peut ne correspondre à aucun langage connu.
Un code de langue est valide s'il ne contient pas certains caractères (deux-point, apostrophe simple ou double, barre oblique renversée ou non, crochet, esperluette, ou le caractère NULL de l'ASCII) et est dans ce cas-là autorisé dans un titre de page.
mw.language.getFallbacksFor
modifiermw.language.getFallbacksFor( code )
Renvoie une liste des codes de langue de MediaWiki pour le code spécifié.
mw.language.isSupportedLanguage
modifiermw.language.isSupportedLanguage( code )
Vérifie si la localisation est disponible dans MediaWiki pour le code de langue indiqué.
Un code de langue est « supporté » si c’est un code « valide » (qui renvoie true via mw.language.isValidCode
), ne contient pas de lettres en majuscule, et a un fichier dans la version courante de MediaWiki.
Il est possible pour un code de langue d’être « supporté » mais pas « reconnu » (c'est-à-dire renvoyant true via mw.language.isKnownLanguageTag
). Veuillez noter aussi que certains codes sont « supportés » malgré que mw.language.isValidBuiltInCode
renvoie false.
mw.language.new
modifiermw.language.new( code )
mw.getLanguage( code )
Crée un nouvel objet Language. Les objets Language n'ont aucune propriété accessible, mais possèdent de nombreuses méthodes qui sont documentées ci-dessous.
mw.language.fetchLanguageNames
modifiermw.language.fetchLanguageNames()
mw.language.fetchLanguageNames( inLanguage )
mw.language.fetchLanguageNames( inLanguage, include )
Cherche la liste des langues reconnues par MediaWiki, et retourne une table de correspondance nom de langue ↔ code de langue.
Par défaut, le nom de langue retourné est celui dans la langue en question ; renseigner un code de langue pour inLanguage
permet de renvoyer tous les noms dans cette langue.
Par défaut, seules les noms de langue connus par MediaWiki sont renvoyés ; renseigner 'all'
pour include
permet le renvoi de toutes les langues disponibles (e.g. depuis mw:Extension:CLDR), tandis que renseigner 'mwfile'
permet de renvoyer seulement les langues qui ont des traductions incluses dans le cœur de MediaWiki ou dans des extensions activées. Pour sélectionner explicitement le défaut, il faut renseigner 'mw'
.
mw.language.isValidBuiltInCode
modifiermw.language.isValidBuiltInCode( code )
Retourne vrai si le code de langue est ou pourrait être disponible dans MediaWiki.
Le code peut en effet ne correspondre à aucun langage connu.
Un code de langue est un "valid built-in code" si c’est un code « valide » (c'est-à-dire qu'on obtient true via mw.language.isValidCode
) ; il est constitué dans ce cas-là seulement de lettres du jeu de codage ASCII, de nombres, de traits d'union, et comporte au moins deux caractères.
Notez que certains codes sont « supportés » (c'est-à-dire qu'on obtient true via mw.language.isSupportedLanguage
) même si la présente fonction renvoie false.
mw.language.fetchLanguageName
modifiermw.language.fetchLanguageName( code, inLanguage )
Le nom complet de la langue du code indiqué : par défaut dans la langue du wiki (autonyme), ou traduit dans la langue indiquée dans inLanguage
.
mw.language.isKnownLanguageTag
modifiermw.language.isKnownLanguageTag( code )
Retourne vrai si le code indiqué est un code reconnu par MediaWiki.
Un code de langue est « connu » si c’est un « valid built-in code » (c'est-à-dire qui renvoie true via mw.language.isValidBuiltInCode
) et renvoie une chaîne non vide via mw.language.fetchLanguageName
.
mw.language.getContentLanguage
modifiermw.language.getContentLanguage()
mw.getContentLanguage()
Retourne un nouvel objet Language correspondant à la langue du wiki sur lequel on se trouve.
Méthode de la librairie Langage
modifierNous commencerons par visualiser le contenu d'un objet Langage grâce au programme suivant :
local p = {}
function p.visualise(frame)
reponse = ""
langage = mw.language.new("essai")
for index, objet in pairs(langage) do
reponse = reponse.."<br>À la clé "..index..", on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Language|visualise}} nous donne :
À la clé convertGrammar, on trouve un objet de type : function
À la clé code, on trouve un objet de type : string
À la clé isRTL, on trouve un objet de type : function
À la clé getArrow, on trouve un objet de type : function
À la clé toBcp47Code, on trouve un objet de type : function
À la clé parseFormattedNumber, on trouve un objet de type : function
À la clé getDirMarkEntity, on trouve un objet de type : function
À la clé getDirMark, on trouve un objet de type : function
À la clé getFallbackLanguages, on trouve un objet de type : function
À la clé ucfirst, on trouve un objet de type : function
À la clé getDir, on trouve un objet de type : function
À la clé caseFold, on trouve un objet de type : function
À la clé grammar, on trouve un objet de type : function
À la clé lc, on trouve un objet de type : function
À la clé plural, on trouve un objet de type : function
À la clé gender, on trouve un objet de type : function
À la clé lcfirst, on trouve un objet de type : function
À la clé formatNum, on trouve un objet de type : function
À la clé formatDate, on trouve un objet de type : function
À la clé getCode, on trouve un objet de type : function
À la clé formatDuration, on trouve un objet de type : function
À la clé uc, on trouve un objet de type : function
À la clé convertPlural, on trouve un objet de type : function
À la clé getDurationIntervals, on trouve un objet de type : function
Nous voyons qu’il y a 22 méthodes et une chaîne de caractère.
Débarrassons nous de la chaîne de caractères
langage.code
modifier
Étudions maintenant les 22 méthodes dans l’ordre où elles sont sorties :
mw.language:convertGrammar
modifierlang:convertGrammar( word, case )
lang:grammar( case, word )
- Notez l’ordre différent des paramètres dans les deux versions.
convertGrammar
utilise le même ordre des paramètres que la méthode de même nom dans l’objet Language alors quegrammar
utilise le même ordre des paramètres que la parser-function de même nom (voir mw:Help:Magic words#Localisation).
Permet de choisir la forme correcte de word
pour l'inflexion case
.
Les valeurs possibles pour word
et case
dépendent de la langue, voir m:Help:Magic words#Language-dependent word conversions et translatewiki:Grammar pour plus de détails.
mw.language:isRTL
modifierlang:isRTL()
Retourne vrai si la langue est écrite de droite à gauche, faux si elle est écrite de gauche à droite.
mw.language:getArrow
modifierlang:getArrow( direction )
Retourne un caractère Unicode correspondant à la direction direction
:
- forwards: "→" ou "←" selon l'orientation de la langue
- backwards: "←" ou "→" selon l'orientation de la langue
- left: "←"
- right: "→"
- up: "↑"
- down: "↓"
mw.language:parseFormattedNumber
modifierlang:parseFormattedNumber( s )
Prend un nombre formaté par lang:formatNum() et retourne le nombre correspondant. C'est une version « compatible avec les conventions de la langue » de tonumber()
.
mw.language:getDirMarkEntity
modifierlang:getDirMarkEntity( opposite )
Retourne "‎" ou "‏", selon la direction de la langue et selon que opposite
est vrai ou faux (si vrai retourne la direction opposée à celle de la langue).
mw.language:getDirMark
modifierlang:getDirMark( opposite )
Retourne une chaîne contenant soit U+200E (le symbole d'écriture de gauche à droite) soit U+200F (le symbole de l'écriture de droite à gauche), selon la direction de la langue et selon que opposite
est vrai ou faux (si vrai retourne la direction opposée à celle de la langue).
mw.language:getFallbackLanguages
modifierlang:getFallbackLanguages()
Renvoie une liste de codes de langue de MediaWiki pour cet objet langue. Équivalent à mw.language.getFallbacksFor( lang:getCode() )
.
mw.language:ucfirst
modifierlang:ucfirst( s )
Convertit le premier caractère de la chaîne en majuscule, comme le fait lang:uc().
mw.language:getDir
modifierlang:getDir()
Retourne "ltr" (left-to-right → de gauche à droite) ou "rtl" (right-to-left → de droite à gauche), selon la direction de la langue.
mw.language:caseFold
modifierlang:caseFold( s )
Convertit la chaîne en une forme appropriée pour une comparaison non sensible à la casse. Notez que le résultat peut ne pas avoir de sens s'il est affiché.
mw.language:grammar
modifier
mw.language:lc
modifierlang:lc( s )
Convertit la chaîne en minuscules, en respectant les règles particulières de la langue correspondante.
Quand la librairie Ustring est chargée, la fonction mw.ustring.lower() est codée en appelant mw.language.getContentLanguage():lc( s )
.
mw.language:plural
modifier
mw.language:gender
modifierlang:gender( what, masculine, feminine, neutral )
lang:gender( what, { masculine, feminine, neutral } )
Permet de choisir la chaîne correspondante au genre indiqué par what
, qui peut être "male", "female" ou un nom d'utilisateur.
mw.language:lcfirst
modifierlang:lcfirst( s )
Convertit le premier caractère de la chaîne en minuscule, comme le fait lang:lc().
mw.language:formatNum
modifierlang:formatNum( n )
Formate un nombre en respectant le groupement des chiffres et le séparateur décimal de la langue correspondante. Par exemple, « 123456.78 » produira « 123,456.78 », « 123.456,78 », ou même « ١٢٣٬٤٥٦٫٧٨ » selon la langue du wiki.
mw.language:formatDate
modifierlang:formatDate( format, timestamp, local )
Formate une date selon le format indiqué dans la chaîne format. Si timestamp
est omis, l’heure actuelle est utilisée. La valeur local
doit être un booléen ou nil. Si local est vrai, l’heure est formatée à partir de l’heure locale du wiki plutôt qu’à partir de l’heure UTC.
La chaîne de formatage et les valeurs supportées pour timestamp
sont identiques à celles du parser function #time. Notez que les antislash ("\") peuvent nécessiter d’être doublés en Lua alors que ce n’est pas nécessaire en wikitexte (car ce caractère a un sens particulier en Lua et non en wikitexte) :
-- ceci génère un retour à la ligne alors que {{#time:\n}} affiche un "n" (n) lang:formatDate( '\n' ) -- ceci affiche un "n" alors que {{#time:\\n}} affiche un "\" -- suivi du numéro du mois. (\12) lang:formatDate( '\\n' ) -- ceci affiche un "\" suivi du numéro du mois, alors que {{#time:\\\\n}} -- affiche deux "\" suivi du numéro du mois. (\\12) lang:formatDate( '\\\\n' )
mw.language:getCode
modifierlang:getCode()
Retourne le code de langue correspondant à cet objet.
mw.language:formatDuration
modifierlang:formatDuration( seconds )
lang:formatDuration( seconds, allowedIntervals )
Découpe une durée (exprimée en secondes) dans des unités utilisables par l'homme, par exemple : 12345 en 3 heures, 25 minutes et 45 secondes, retournant le résultat dans une chaîne.
allowedIntervals
, si indiqué, est une table avec des valeurs nommant les unités d'intervalle à utiliser dans la réponse. Cela comprend : 'millennia', 'centuries', 'decades', 'years', 'weeks', 'days', 'hours', 'minutes', et 'seconds'.
mw.language:uc
modifierlang:uc( s )
Convertit la chaîne en majuscules, en respectant les règles particulières de la langue correspondante.
Quand la librairie Ustring est chargée, la fonction mw.ustring.upper() est codée en appelant mw.language.getContentLanguage():uc( s )
.
mw.language:convertPlural
modifierlang:convertPlural( n, ... )
lang:convertPlural( n, forms )
lang:plural( n, ... )
lang:plural( n, forms )
Permet de choisir la forme gramaticale adaptée depuis forms
(qui doit être une séquence) ou ...
en se basant sur le nombre n
. Par exemple en anglais, vous pouvez utiliser n .. ' ' .. lang:plural( n, 'sock', 'socks' )
ou n .. ' ' .. lang:plural( n, { 'sock', 'socks' } )
pour générer un texte grammaticalement correct qu’il y ait 1 ou 200 "socks".
Le nombre de valeurs nécessaires pour la séquence dépend de la langue ; voir m:Help:Magic words#Language-dependent word conversions et translatewiki:FAQ#PLURAL pour plus de détails.
mw.language:getDurationIntervals
modifierlang:getDurationIntervals( seconds )
lang:getDurationIntervals( seconds, allowedIntervals )
Découpe une durée exprimée en secondes en unités lisibles par l'homme, et retourne le résultat dans une table dont les éléments sont classés par unité. Par exemple 12345 retournera les éléments 3 heures, 25 minutes et 45 secondes.
allowedIntervals
, si précisé, est une table avec comme valeurs les noms des unités à utiliser : 'millennia', 'centuries', 'decades', 'years', 'days', 'hours', 'minutes', and 'seconds'.
Librairie Message
Présentation de la librairie message
modifierCette librairie est une interface aux versions localisées des messages et à l'espace de noms MediaWiki:.
Les fonctions documentées comme mw.message.name
sont disponibles dans la table globale mw.message
. Les fonctions documentées comme mw.message:name
sont des méthodes des objets Message (voir mw.message.new
).
Fonctions de la librairie Message
modifierLes exemples de ce chapitre se trouveront dans le Module:Message
Nous commencerons par visualiser le contenu de la librairie Message grâce au programme suivant :
local p = {}
function p.visualisation(frame)
reponse = ""
for index, objet in pairs(mw.message) do
reponse = reponse.."<br />À la clé "..index..", on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Message|visualisation}} nous donne :
À la clé numParam, on trouve un objet de type : function
À la clé getDefaultLanguage, on trouve un objet de type : function
À la clé rawParam, on trouve un objet de type : function
À la clé newFallbackSequence, on trouve un objet de type : function
À la clé newRawMessage, on trouve un objet de type : function
À la clé new, on trouve un objet de type : function
Nous voyons que nous avons 6 fonctions.
Étudions ces fonctions :
mw.message.new
modifiermw.message.new( key, ... )
Crée un nouvel objet Message pour le message indiqué key
.
L'objet message n'a aucune propriété mais de nombreuses méthodes documentées ci-dessous.
mw.message.newFallbackSequence
modifiermw.message.newFallbackSequence( ... )
Crée un nouvel objet message pour les messages indiqués (le premier qui existe sera utilisé).
L'objet message n'a aucune propriété mais de nombreuses méthodes documentées ci-dessous.
mw.message.newRawMessage
modifiermw.message.newRawMessage( msg, ... )
Crée un nouvel objet message en utilisant le texte indiqué plutôt qu'en cherchant un message internationalisé. Les paramètres additionnels sont passés à la méthode params()
du nouvel objet.
L'objet message n'a aucune propriété mais de nombreuses méthodes documentées ci-dessous.
mw.message.rawParam
modifiermw.message.rawParam( value )
Traite la valeur value de façon qu'elle ne soit pas interprétée comme du wikitexte par msg:parse()
.
mw.message.numParam
modifiermw.message.numParam( value )
Traite la valeur value de façon à ce qu'elle soit automatiquement formatée comme avec lang:formatNum()
. Notez que ceci ne dépend pas de la librairie Language actuellement disponible dans Scribunto.
mw.message.getDefaultLanguage
modifiermw.message.getDefaultLanguage()
Retourne un objet Language pour la langue courante.
Méthodes de la librairie message
modifierNous commencerons par visualiser le contenu d'un objet Message grâce au programme suivant :
local p = {}
function p.visualise(frame)
reponse = ""
message = mw.message.new("essai")
for index, objet in pairs(message) do
reponse = reponse.."<br />À la clé "..index..", on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Message|visualise}} nous donne :
À la clé inLanguage, on trouve un objet de type : function
À la clé isDisabled, on trouve un objet de type : function
À la clé isBlank, on trouve un objet de type : function
À la clé exists, on trouve un objet de type : function
À la clé useDatabase, on trouve un objet de type : function
À la clé plain, on trouve un objet de type : function
À la clé numParams, on trouve un objet de type : function
À la clé rawParams, on trouve un objet de type : function
À la clé params, on trouve un objet de type : function
Nous voyons qu’il y a 9 méthodes.
Étudions ces méthodes :
mw.message:params
modifiermsg:params( ... )
msg:params( params )
Ajoute des paramètres au message, qui peuvent être passés en tant que paramètres individuels ou dans une séquence. Les paramètres doivent être des nombres, chaînes ou des valeurs spéciales retournées par mw.message.numParam() ou mw.message.rawParam(). Si une table non associative est utilisée, les paramètres doivent être directement présents dans la table ; les références utilisant la méta-méthode __index ne marcheront pas.
Retourne l’objet msg
pour autoriser des appels chaînés.
mw.message:rawParams
modifiermsg:rawParams( ... )
msg:rawParams( params )
Comme :params() mais chaque paramètre est passé préalablement par mw.message.rawParam().
Retourne l’objet msg
pour autoriser des appels chaînés.
mw.message:numParams
modifiermsg:numParams( ... )
msg:numParams( params )
Comme :params() mais chaque paramètre est passé préalablement par mw.message.numParam().
Retourne l’objet msg
pour autoriser des appels chaînés.
mw.message:inLanguage
modifiermsg:inLanguage( lang )
Précise la langue à utiliser pour traiter le message. lang
peut être une chaîne ou une table avec une méthode getCode()
(par exemple un objet Language).
La langue par défaut est celle retournée par mw.message.getDefaultLanguage()
.
Retourne l’objet msg
pour autoriser des appels chaînés.
mw.message:useDatabase
modifiermsg:useDatabase( bool )
Précise s'il faut chercher les messages dans l'espace de noms MediaWiki: (donc regarder dans la base de données) ou seulement utiliser les messages par défaut distribués avec MediaWiki.
La valeur par défaut est true.
Retourne l’objet msg
pour autoriser des appels chaînés.
mw.message:plain
modifiermsg:plain()
Remplace les paramètres et retourne le message tel quel en wikitexte. Les appels à des modèles et à des parser-functions sont laissés intacts.
mw.message:exists
modifiermsg:exists()
Retourne un booléen indiquant si la clé de message existe.
mw.message:isBlank
modifiermsg:isBlank()
Retourne un booléen indiquant si la clé de message correspond à un contenu. Retourne vrai si la clé n'existe pas ou si le message est la chaîne vide.
mw.message:isDisabled
modifiermsg:isDisabled()
Retourne un booléen indiquant si la clé de message est désactivée. Retourne vrai si la clé n'existe pas, si le message est la chaîne vide ou la chaîne "-".
Librairie Site
Présentation de la librairie Site
modifierLa librairie Site permet d'obtenir des renseignements sur le projet sur lequel le programme Lua tourne.
Objets de la librairie Site
modifierLes exemples de ce chapitre se trouveront dans le Module:Site
Nous commencerons par visualiser le contenu de la librairie Site grâce au programme suivant :
local p = {}
function p.visualisation(frame)
reponse = ""
for index, objet in pairs(mw.site) do
reponse = reponse.."<br />À la clé "..index..", on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Site|visualisation}} nous donne :
À la clé stats, on trouve un objet de type : table
À la clé siteName, on trouve un objet de type : string
À la clé currentVersion, on trouve un objet de type : string
À la clé interwikiMap, on trouve un objet de type : function
À la clé talkNamespaces, on trouve un objet de type : table
À la clé scriptPath, on trouve un objet de type : string
À la clé server, on trouve un objet de type : string
À la clé stylePath, on trouve un objet de type : string
À la clé namespaces, on trouve un objet de type : table
À la clé subjectNamespaces, on trouve un objet de type : table
À la clé contentNamespaces, on trouve un objet de type : table
Nous voyons que nous avons une fonction, 5 tables et 5 chaînes de caractères.
Étudions ces objets :
mw.site.stats
modifiermw.site.stats est une table contenant diverses statistiques. Pour nous mettre en appétit, nous commencerons donc par un programme permettant de visualiser le contenu de cette table.
local p = {}
function p.visustat()
reponse = ""
for index, objet in pairs(mw.site.stats) do
reponse = reponse.."<br />À la clé "..index..", on trouve un objet de type : "..type(objet)
end
return reponse
end
return p
{{#invoke:Site|visustat}} nous donne :
À la clé articles, on trouve un objet de type : number
À la clé admins, on trouve un objet de type : number
À la clé usersInGroup, on trouve un objet de type : function
À la clé edits, on trouve un objet de type : number
À la clé users, on trouve un objet de type : number
À la clé pagesInCategory, on trouve un objet de type : function
À la clé files, on trouve un objet de type : number
À la clé pagesInNamespace, on trouve un objet de type : function
À la clé pages, on trouve un objet de type : number
À la clé activeUsers, on trouve un objet de type : number
Nous voyons déjà qu’il y a 7 nombres. En fait, il aurait pu y en avoir 8.
Les nombres disponibles de cette table sont :
- pages : nombre de pages dans le wiki
- articles : nombre d’articles dans le wiki
- files : nombre de fichiers dans le wiki
- edits : nombre d'éditions dans le wiki
- views : nombre de visualisation dans le wiki. Non disponible si $wgDisableCounters est présent
- users : nombre d'utilisateurs dans le wiki
- activeUsers : nombre d'utilisateurs actifs dans le wiki
- admins : nombre d'utilisateurs dans le groupe 'sysop' dans le wiki
Par curiosité, écrivons donc un autre programme donnant les statistiques de la Wikiversité :
local p = {}
function p.visuversite()
reponse = "La Wikiversité comprend :<br />"
reponse = reponse..mw.site.stats.pages.." pages.<br />"
reponse = reponse..mw.site.stats.articles.." articles.<br />"
reponse = reponse..mw.site.stats.files.." fichiers.<br />"
reponse = reponse..mw.site.stats.edits.." éditions.<br />"
reponse = reponse..mw.site.stats.users.." utilisateurs.<br />"
reponse = reponse..mw.site.stats.activeUsers.." utilisateurs actifs.<br />"
reponse = reponse..mw.site.stats.admins.." arministrateurs.<br />"
return reponse
end
return p
{{#invoke:Site|visuversite}} nous donne : La Wikiversité comprend :
55332 pages.
16924 articles.
83 fichiers.
948864 éditions.
77074 utilisateurs.
52 utilisateurs actifs.
9 administrateurs.
Dans notre table mw.site.stats, nous avons vu qu’il y avait aussi trois fonctions. Les trois prochains paragraphes seront consacrés à étudier ces trois fonctions :
mw.site.stats.usersInGroup
modifiermw.site.stats.usersInGroup( group )
Retourne le nombre d'utilisateurs dans le groupe indiqué.
mw.site.stats.pagesInCategory
modifiermw.site.stats.pagesInCategory( category, which )
- Cette fonction est coûteuse
Retourne des statistiques sur la catégorie. Si which
est non précisé, vaut nil, ou "*", cette fonction retourne une table avec les éléments suivants :
- all : total des éléments ci-dessous
- subcats : nombre de sous-catégories
- files : nombre de fichiers
- pages : nombre de pages
Si which
est l'une des clés ci-dessus seule la valeur correspondante est retournée, sous forme de nombre.
Chaque appel à cette fonction incrémente le compteur de « fonctions coûteuses ».
mw.site.stats.pagesInNamespace
modifiermw.site.stats.pagesInNamespace( ns )
Retourne le nombre de pages dans l'espace de noms indiqué (indiqué par son numéro).
Nous allons enfin savoir combien il y a de pages dans chaque espace de la Wikiversité.
local p = {}
function p.visuespace()
reponse = "La Wikiversité comprend :<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(0).." pages dans l'espace principal.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(1).." pages dans l'espace discussion.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(2).." pages dans l'espace utilisateur.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(3).." pages dans l'espace discussion utilisateur.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(4).." pages dans l'espace Wikiversité.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(5).." pages dans l'espace discussion Wikiversité.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(6).." pages dans l'espace fichier.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(7).." pages dans l'espace discussion fichier.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(8).." pages dans l'espace Média Wiki.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(9).." pages dans l'espace discussion Média Wiki.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(10).." pages dans l'espace modèle.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(11).." pages dans l'espace discussion modèle.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(12).." pages dans l'espace aide.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(13).." pages dans l'espace discussion aide.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(14).." pages dans l'espace catégorie.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(15).." pages dans l'espace discussion catégorie.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(102).." pages dans l'espace projet.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(103).." pages dans l'espace discussion projet.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(104).." pages dans l'espace recherche.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(105).." pages dans l'espace discussion recherche.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(106).." pages dans l'espace Faculté.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(107).." pages dans l'espace discussion Faculté.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(108).." pages dans l'espace Département.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(109).." pages dans l'espace discussion Département.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(110).." pages dans l'espace Transwiki.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(111).." pages dans l'espace discussion Transwiki.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(828).." pages dans l'espace module.<br />"
reponse = reponse..mw.site.stats.pagesInNamespace(829).." pages dans l'espace discussion module.<br />"
return reponse
end
return p
{{#invoke:Site|visuespace}} nous donne : La Wikiversité comprend :
20815 pages dans l'espace principal.
3498 pages dans l'espace discussion.
4846 pages dans l'espace utilisateur.
7517 pages dans l'espace discussion utilisateur.
1738 pages dans l'espace Wikiversité.
144 pages dans l'espace discussion Wikiversité.
83 pages dans l'espace fichier.
5 pages dans l'espace discussion fichier.
194 pages dans l'espace Média Wiki.
21 pages dans l'espace discussion Média Wiki.
3331 pages dans l'espace modèle.
192 pages dans l'espace discussion modèle.
213 pages dans l'espace aide.
33 pages dans l'espace discussion aide.
5018 pages dans l'espace catégorie.
751 pages dans l'espace discussion catégorie.
963 pages dans l'espace projet.
164 pages dans l'espace discussion projet.
1909 pages dans l'espace recherche.
484 pages dans l'espace discussion recherche.
209 pages dans l'espace Faculté.
86 pages dans l'espace discussion Faculté.
2110 pages dans l'espace Département.
147 pages dans l'espace discussion Département.
10 pages dans l'espace Transwiki.
5 pages dans l'espace discussion Transwiki.
380 pages dans l'espace module.
44 pages dans l'espace discussion module.
Remarque : Contrairement à ce que l’on voit ci-dessus, le mot magique {{NUMBEROFARTICLES}} nous indique seulement 16 924 pages dans l'espace principal. C'est parce-que les redirections ne sont pas comptées.
mw.site.siteName
modifierLa valeur de $wgSitename.
mw.site.currentVersion
modifierUne chaîne contenant la version actuelle de MediaWiki.
mw.site.interwikiMap
modifiermw.site.interwikiMap( filter )
Returns a table holding data about available interwiki prefixes. If filter
is the string "local", then only data for local interwiki prefixes is returned. If filter
is the string "!local", then only data for non-local prefixes is returned. If no filter is specified, data for all prefixes is returned. A "local" prefix in this context is one that is for the same project. For example, on the English Wikipedia, other-language Wikipedias are considered local, while Wiktionary and such are not.
Keys in the table returned by this function are interwiki prefixes, and the values are subtables with the following properties:
- prefix - the interwiki prefix.
- url - the URL that the interwiki points to. The page name is represented by the parameter $1.
- isProtocolRelative - a boolean showing whether the URL is stored as a protocol-relative URL in the database. Note that if this is true, the URL in the "url" parameter will not be protocol-relative. Instead it will start with "http" or "https" depending on the protocol that the page is accessed with.
- isLocal - whether the URL is for a site in the current project.
- isCurrentWiki - whether the URL is for the current wiki.
- isTranscludable - whether pages using this interwiki prefix are transcludable.
- isExtraLanguageLink - whether the interwiki is listed in $wgExtraInterlanguageLinkPrefixes.
- displayText - for links listed in $wgExtraInterlanguageLinkPrefixes, this is the display text shown for the interlanguage link. Nil if not specified.
- tooltip - for links listed in $wgExtraInterlanguageLinkPrefixes, this is the tooltip text shown when users hover over the interlanguage link. Nil if not specified.
mw.site.talkNamespaces
modifierTable contenant uniquement les espaces de nom de discussion, indexé par leur numéro. Voir mw.site.namespaces.
mw.site.scriptPath
modifierLa valeur de $wgScriptPath.
mw.site.server
modifierLa valeur de $wgServer.
mw.site.stylePath
modifierLa valeur de $wgStylePath.
mw.site.namespaces
modifierTable contenant les informations sur tous les espaces de noms, indexés par leur numéro :
- id : numéro de l'espace de noms
- name : nom local de l'espace de noms
- canonicalName : nom générique de l'espace de noms
- displayName : fixé pour l'espace de noms 0 : le nom à utiliser pour affichage, car name vaut la chaîne vide sur la plupart des wikis
- hasSubpages : est-ce que les sous-pages sont actives pour cet espace de noms
- hasGenderDistinction : est-ce que l'espace de noms a des alias différents selon le genre
- isCapitalized : est-ce que la première lettre des pages de cet espace de noms doit être mise en majuscule
- isContent : est-ce un espace de noms pour du contenu
- isIncludable : est-ce que les pages de cet espace de noms peuvent être transclues
- isMovable : est-ce que les pages de cet espace de noms peuvent être déplacées
- isSubject : est-ce que c’est un espace de noms de sujet (subject namespace)
- isTalk : est un espace de noms de discussion
- defaultContentModel : Le type de contenu par défaut pour l'espace de noms, sous forme de chaîne. (Voir mw:Manual:ContentHandler.)
- aliases : liste des alias pour cet espace de noms
- subject : référence vers l'espace de noms sujet de cet espace de noms
- talk : référence vers l'espace de noms de discussion de cet espace de noms
- associated : référence à l'espace de noms associé (Discussion MediaWiki pour MediaWiki par exemple, et vice-versa)
Une méta-table est présente pour permettre de chercher un espace de noms par son nom (localisé ou générique). Par exemple mw.site.namespaces[4]
et mw.site.namespaces.Project
retourneront la même chose.
mw.site.subjectNamespaces
modifierTable contenant uniquement les espaces de nom de sujets, indexés par leur numéro. Voir mw.site.namespaces.
mw.site.contentNamespaces
modifierTable contenant uniquement les espaces de nom de contenu, indexés par leur numéro. Voir mw.site.namespaces.
Quelques compléments
Ce chapitre est réservé à tout ce qui n'a pas pu être mis dans les chapitres précédents.
Librairies chargeables
modifierCes librairies ne sont pas incluses par défaut, mais peuvent être chargées si besoin en utilisant require()
.
bit32
modifierCeci est une émulation de la librairie bit32
de Lua 5.2. On peut la charger avec :
bit32 = require( 'bit32' )
La librairie bit32 fournit des opérations binaires sur des entiers non signés 32bits. Les nombres en entrée sont tronqués en entiers (d'une façon non spécifiée) et ramenés entre 0 et 232-1 par une opération de modulo. Les valeurs retournées sont également dans cet intervalle de valeurs.
Quand les bits sont numérotés (comme dans bit32.extract()), 0 est le bit de poids faible (celui correspondant à la valeur 20) et 31 est celui de poids fort (celui valant 231).
bit32.band
modifierbit32.band( ... )
Retourne le ET binaire de ses paramètres : le résultat a un bit donné à 1 si et seulement si le même bit de chaque paramètre est à 1.
Appelée sans paramètre, cette fonction retourne tous les bits à 1.
bit32.bnot
modifierbit32.bnot( x )
Retourne le complément binaire de x
.
bit32.bor
modifierbit32.bor( ... )
Retourne le OU binaire de tous ses paramètres : le résultat a un bit donné à 1 si au moins un des paramètres a le même bit à 1.
Appelée sans paramètre, cette fonction retourne tous les bits à 0.
bit32.btest
modifierbit32.btest( ... )
Équivalent à bit32.band( ... ) ~= 0
bit32.bxor
modifierbit32.bxor( ... )
Retourne le OU EXCLUSIF binaire de ses paramètres : le résultat a un bit donné à 1 si le nombre de paramètres ayant ce même bit à 1 est impair.
Appelée sans paramètre, cette fonction retourne tous les bits à 0.
bit32.extract
modifierbit32.extract( n, field, width )
Extrait width
bits de n
, en commençant au bit field
. Accéder à des bits en dehors de l'intervalle 0 à 31 est une erreur.
Si width
est non précisé, sa valeur par défaut est 1.
bit32.replace
modifierbit32.replace( n, v, field, width )
Remplace width
bits de n
, en commençant au bit field
, avec les width
premiers bits de v
. Accéder à des bits en dehors de l'intervalle 0 à 31 est une erreur.
Si width
est non précisé, sa valeur par défaut est 1.
bit32.lshift
modifierbit32.lshift( n, disp )
Retourne le nombre n
décalé de disp
bits vers la gauche. Ceci est un décalage logique : les bits insérés valent 0. Ceci est en général équivalent à multiplier par 2disp
.
Notez qu'un déplacement au-delà de 31 donne 0.
bit32.rshift
modifierbit32.rshift( n, disp )
Retourne le nombre n
décalé de disp
bits vers la droite. Ceci est un décalage logique : les bits insérés valent 0. Ceci est en général équivalent à diviser par 2disp
.
Notez qu'un déplacement au-delà de 31 donne 0.
bit32.arshift
modifierbit32.arshift( n, disp )
Retourne le nombre n
décalé de disp
bits vers la droite. Ceci est un décalage arithmétique : si disp
est positif, les bits insérés seront les mêmes que le bit 31 du nombre initial.
Notez qu'un déplacement au-delà de 31 donne 0 ou 4294967295.
bit32.lrotate
modifierbit32.lrotate( n, disp )
Retourne le nombre n
décalé circulairement de disp
bits vers la gauche.
Notez que les rotations sont équivalentes modulo 32 : une rotation de 32 est identique à une rotation de 0, une rotation de 33 à une rotation de 1…
bit32.rrotate
modifierbit32.rrotate( n, disp )
Retourne le nombre n
décalé circulairement de disp
bits vers la droite.
Notez que les rotations sont équivalentes modulo 32 : une rotation de 32 est identique à une rotation de 0, une rotation de 33 à une rotation de 1…
libraryUtil
modifierCette librairie contient des méthodes utiles pour implémenter des librairies Scribunto. Elle peut être chargée avec :
libraryUtil = require( 'libraryUtil' )
libraryUtil.checkType
modifierlibraryUtil.checkType( name, argIdx, arg, expectType, nilOk )
Génère une erreur si type( arg )
ne correspond pas à expectType
. De plus, aucune erreur n'est générée si arg
est nil et si nilOk
est vrai.
name
est le nom de la fonction qui appelle cette fonction, et argIdx
est la position du paramètre dans la liste des paramètres. Ils sont utilisés pour générer le message d'erreur.
libraryUtil.checkTypeMulti
modifierlibraryUtil.checkTypeMulti( name, argIdx, arg, expectTypes )
Lève une erreur si type( arg )
ne reconnait aucune des chaînes dans le tableau expectTypes
.
Cela sert pour les arguments qui ont plus d'un type valide.
libraryUtil.checkTypeForIndex
modifierlibraryUtil.checkTypeForIndex( index, value, expectType )
Génère une erreur si type( value )
ne correspond pas à expectType
.
Ceci sert à implémenter une méta-méthode __newindex
.
libraryUtil.checkTypeForNamedArg
modifierlibraryUtil.checkTypeForNamedArg( name, argName, arg, expectType, nilOk )
Génère une erreur si type( arg )
ne correspond pas à expectType
. De plus, aucune erreur n'est générée si arg
vaut nil et que nilOk
vaut true.
Cette fonction est un équivalent de la fonction libraryUtil.checkType()
pour les méthodes appelées en utilisant la syntaxe des paramètres nommés (func{ name = value }
).
libraryUtil.makeCheckSelfFunction
modifierlibraryUtil.makeCheckSelfFunction( libraryName, varName, selfObj, selfObjDesc )
Ceci sert à implémenter des "méthodes" sur des tables destinées à être appelées avec la syntaxe obj:method()
. Il retourne une fonction qui peut être appelée « en haut » de ces méthodes avec le paramètre self
et le nom de la méthode, et génère une erreur si l’objet self
n’est pas selfObj
.
Cette fonction est en général utilisé dans les constructeurs de fonctions de librairies :
function myLibrary.new() local obj = {} local checkSelf = libraryUtil.makeCheckSelfFunction( 'myLibrary', 'obj', obj, 'myLibrary object' ) function obj:method() checkSelf( self, 'method' ) end function obj:method2() checkSelf( self, 'method2' ) end return obj end
luabit
modifierLes librairies "bit" et "hex" de luabit peuvent être chargées avec :
bit = require( 'luabit.bit' ) hex = require( 'luabit.hex' )
Notez que la librairie bit32 contient les mêmes opérations que "luabit.bit", et que les opérations dans "luabit.hex" peuvent être réalisées en utilisant string.format()
et tonumber()
.
Le module "noki" n’est pas disponible car il n'est d'aucune utilité en Scribunto. Le module "utf8" n'est également pas disponible car redondant avec la librairie Ustring.
ustring
modifierL'interface Lua native pour la librairie Ustring peut être chargée comme suit :
ustring = require( 'ustring' )
Dans tous les cas, la librairie Ustring (mw.ustring
) devrait être utilisée à la place, car elle remplace de nombres fonctions plus lentes par des appels au code PHP.
Débogage
Ce chapitre regroupe tous les messages d'erreur et leurs solutions.
attempt to compare number with string
modifierConvertir soit la chaine avec tonumber()
, soit le nombre avec tostring()
.
attempt to index field 'args' (a nil value)
modifierIl faut lever l'exception nulle sur la variable avant d'accéder à sa propriété "args".
bad argument #1 to 'find' (string expected, got table)
modifierConvertir la table en ajoutant ".text" en suffixe.
bad argument #2 to 'tonumber' (base out of range)
modifierCela se produit quand il faut convertir un tableau en chaine, ex : tonumber(t)
→ tonumber(t[1])
.
Erreur Lua : Cannot pass circular reference to PHP
modifierSe produit quand on appelle un titre comme un texte, par exemple mw.title.getCurrentTitle()
au lieu de mw.title.getCurrentTitle().fullText
.
Le module a renvoyé une valeur nil. Il est supposé renvoyer un tableau d’exportations
modifierIl manque le return p
à la fin du module qui commence par local p = {}
.
Modèle en boucle détecté
modifierUn module s’appelle lui-même, il faut donc isoler cette partie récursive du modèle qui l'invoque entre balises <noinclude>...</noinclude>.
Voir aussi
modifier
GFDL | Vous avez la permission de copier, distribuer et/ou modifier ce document selon les termes de la licence de documentation libre GNU, version 1.2 ou plus récente publiée par la Free Software Foundation ; sans sections inaltérables, sans texte de première page de couverture et sans texte de dernière page de couverture. |