Aller au contenu

Initiation au Lua avec Scribunto/Version imprimable

Leçons de niveau 10
Une page de Wikiversité, la communauté pédagogique libre.
Image logo
Ceci est la version imprimable de Initiation au Lua avec Scribunto.
  • Si vous imprimez cette page, choisissez « Aperçu avant impression » dans votre navigateur, ou cliquez sur le lien Version imprimable dans la boîte à outils, vous verrez cette page sans ce message, ni éléments de navigation sur la gauche ou en haut.
  • Cliquez sur Rafraîchir cette page pour obtenir la dernière version du cours.
  • Pour plus d'informations sur les version imprimables, y compris la manière d'obtenir une version PDF, vous pouvez lire l’article Versions imprimables.


Initiation au Lua avec Scribunto

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

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. Une copie de cette licence est inclue dans l'annexe nommée « Licence de documentation libre GNU ».

Premières notions

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 1
Leçon : Initiation au Lua avec Scribunto
Retour auSommaire
Chap. suiv. :Mise au point d'un module

Exercices :

Premiers pas
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

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

[modifier | modifier le wikicode]

L'espace module

[modifier | modifier le wikicode]

Pour é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

[modifier | modifier le wikicode]

Nous 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.

Tout 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

[modifier | modifier le wikicode]

Nous 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

[modifier | modifier le wikicode]

Dans 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

[modifier | modifier le wikicode]

Exemple avec un seul paramètre

[modifier | modifier le wikicode]

Pour 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

[modifier | modifier le wikicode]

Nous 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

[modifier | modifier le wikicode]

La 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

[modifier | modifier le wikicode]

Nous 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 | modifier le wikicode]

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

[modifier | modifier le wikicode]

Il 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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 2
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Premières notions
Chap. suiv. :Tables et fonctions

Exercices :

Recherche d'erreurs
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

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

[modifier | modifier le wikicode]

Une 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:

C'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

[modifier | modifier le wikicode]

Le 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

[modifier | modifier le wikicode]

Nous 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

[modifier | modifier le wikicode]
Figure 1
Panneau d’avertissement Pour 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.


Figure 2

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

[modifier | modifier le wikicode]

Aprè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

[modifier | modifier le wikicode]

Vous 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

[modifier | modifier le wikicode]

Nous 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

[modifier | modifier le wikicode]

Lorsque 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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 3
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Mise au point d'un module
Chap. suiv. :Structures de contrôle

Exercices :

Sur les tables et les fonctions
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

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

[modifier | modifier le wikicode]

Au 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

[modifier | modifier le wikicode]

Nous venons de voir que les index des tables peuvent s'écrire à l'aide de nombres. Mais il est possible aussi de les écrire à l'aide de chaîne de caractères. Reprenons notre Module:Traduit. Nous pouvons alors imaginer un moyen astucieux de traduire un mot en anglais en se servant de ce mot directement comme index d'une table qui contiendrait toutes les traductions. Par exemple, dans une table tab avec l'index "chien", on accéderait à l'emplacement tab["chien"] où se trouverait la chaîne de caractère dog

Sur ce principe, écrivons donc un nouveau programme de traduction des jours de la semaine en anglais :

local p = {}
local sem = { ["lundi"] = "monday", ["mardi"] = "tuesday", ["mercredi"] = "wednesday", ["jeudi"] = "thursday", ["vendredi"] = "friday", ["samedi"] = "saturday", ["dimanche"] = "sunday"}

function p.anglais(frame)
	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

[modifier | modifier le wikicode]

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

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

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

Mathématiquement, ce genre de table peut correspondre à des matrices.

Par exemple, la matrice :

se déclarera :

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

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


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

local B = {}

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

B[3][1] = 9

On obtiendra une erreur de script.


Fonctions relatives aux tables

[modifier | modifier le wikicode]

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

  • table.insert(t, ligne) : incrémente la table avec "ligne".
  • table.remove(t,ligne) : retire un élément de la table.
  • table.concat(t, séparateur) : convertit la table en une chaîne de caractè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

[modifier | modifier le wikicode]

Fonctions appelées par une fonction

[modifier | modifier le wikicode]

Nous 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

[modifier | modifier le wikicode]

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

function f(x,y,z)
	return x^2+y^2+z^2
end

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

function f(x)
	return x^2,2x,5x-3
end

Nous voyons que la forme que prennent les valeurs en sortant est similaire à la forme que prennent les paramètres à l'entrée. Nous pouvons mettre ceci à profit en emboîtant des fonctions (fonctions composées). Prenons un exemple pour voir si cela marche bien :

local p = {}

function g(x,y,z)
	return 2*x+y+3*z
end

function h(x)
	return x,2*x,x
end

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

[modifier | modifier le wikicode]

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

local p = {}

function fact(n)
    if n == 0 then
        return 1 -- on renvoie la valeur 1 quand le paramètre vaut 0
    else
        return n * fact(n - 1)
    end
end

function p.factorielle(frame)
	return fact(frame.args[1])
end

return p

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

4! = 4✕3! = 4✕3✕2! = 4✕3✕2✕1! = 4✕3✕2✕1✕0! = 4✕3✕2✕1✕1 = 4✕3✕2✕1 = 24


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

Question : Dans le programme précédent, n'aurait-on pas pu rendre directement la fonction p.factorielle récurssive au lieu de lui faire appeler une autre fonction récursive, ici la fonction fact ?

Réponse : Non, car la fonction p.factorielle a pour argument frame et est donc, par conséquent, dédiée à recevoir des informations de l'extérieur du module et pas de l'intérieur. Elle n'est donc pas appelable de l'intérieur du module et donc ne peut pas s'appeler elle-même.


Tables de fonctions

[modifier | modifier le wikicode]

En lua, nous pouvons créer des tables de fonctions. Nous devrions commencer à y être habitués car depuis le début de cette leçon, nous utilisons une table que nous avons appelé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

[modifier | modifier le wikicode]

Une fonction peut recevoir en argument tout type d'objet. Par conséquent, une fonction peut recevoir une table en argument. Il y a toutefois une petite différence dans le passage d'une table en argument dans une fonction. Les tables sont passés par référence alors que la plupart des autres variables sont passées par valeur. Nous allons nous efforcer de bien comprendre ce que cela signifie dans la suite de ce paragraphe.

Lorsqu'on passe un objet par valeur à une fonction, cela signifie que la fonction recopie l’objet dans la variable déclarée en argument dans la fonction. par exemple reprenons la fonction f défini plus haut :

function f(x)
	return x^2
end

Si dans une autre fonction, on écrit

resultat = 7+f(a)

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


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

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

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

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

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

[modifier | modifier le wikicode]

Nous 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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 4
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Tables et fonctions
Chap. suiv. :Fonctions basiques

Exercices :

Sur les structures de contrôle
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

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

[modifier | modifier le wikicode]

Nous avons déjà étudié cette structure au premier chapitre car il est difficile de s'en passer puisqu'elle permet de prendre une décision en fonction d'une condition. Nous rappelons que, en français la structure if condition then instruction1 else instruction2 end signifie : Si une condition est remplie exécuter instruction1 sinon exécuter instruction2. Dans le Module:Balance, nous avons donné l'exemple suivant :

local p = {}

function p.alerte3(frame)
	local poids = tonumber(frame.args[1])
	local reponse
	if poids < 55 then
		reponse = "Votre poids est acceptable"
	else
		reponse = "Attention, vous commencez à grossir !"
	end
	return reponse
end

return p

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

Nous rajouterons, dans ce chapitre, qu’il est possible aussi d'emboîter les 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

[modifier | modifier le wikicode]

La structure while..do permet de répéter un ensemble d'instructions tant qu'une condition est vraie. Sa syntaxe générale est : while condition do instructions end, ce qui signifie, en français : Tant que condition faire instructions fin.

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

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

[modifier | modifier le wikicode]

La structure : repeat..until permet de répéter un ensemble d'instructions jusqu'à ce qu'une condition soit vraie. Sa syntaxe générale est : repeat instructions until condition, ce qui signifie, en français : Répéter instructions jusqu'à condition.

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

local p = {}

function p.somme2(frame)
	local limite = tonumber(frame.args[1])
	local reponse = 0
	local entier = 1
	repeat
		reponse = reponse + entier
		entier = entier + 1
	until reponse > limite
	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

[modifier | modifier le wikicode]

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

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

Première forme de la boucle for

[modifier | modifier le wikicode]

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

for index = m, n, p do
	instructions
end

m, n, p étant trois entiers. p est facultatif. Pour chaque passage dans la boucle, la variable index prendra toutes les valeurs de m à n en étant incrémenté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

[modifier | modifier le wikicode]

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

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

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

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

[modifier | modifier le wikicode]

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

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

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

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

[modifier | modifier le wikicode]

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

c = a and b

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

a b c
false false false
false true false
true false false
true true true


L'opérateur or

[modifier | modifier le wikicode]

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

c = a or b

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

a b c
false false false
false true true
true false true
true true true


L'opérateur not

[modifier | modifier le wikicode]

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

c = not a

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

a c
false true
true false


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

local p = {}

function p.essai3(frame)
	local n = tonumber(frame.args[1])
	if 1 < n and n < 8 then
		return "Le nombre est strictement compris entre 1 et 8"
	else
		return "Le nombre n’est pas strictement compris entre 1 et 8"
	end
end

return p

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


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

[modifier | modifier le wikicode]

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

Par exemple, nous avons écrit plus haut :

	local correct = 2 > 1
	local panne = 2 < 1

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


> est l'opérateur "strictement supérieur". Nous avons aussi l'opérateur "supérieur ou égal" qui se noterait >=.

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


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

  1. 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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 5
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Structures de contrôle
Chap. suiv. :Chaînes de caractères

Exercices :

Sur les fonctions basiques
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

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


La 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


Nous 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 !


La 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.


C'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.


La 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).


Nous 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.


La 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.


La 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.


Nous 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.


La 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


Cette 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


Cette 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


Cette 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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 6
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Fonctions basiques
Chap. suiv. :Fonctions mathématiques

Exercices :

Sur les chaînes de caractères
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

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

[modifier | modifier le wikicode]

Une 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
Panneau d’avertissement 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

[modifier | modifier le wikicode]

La 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 :


Cette 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.


Cette 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.


Cette 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.


En Unicode, on utilisera la fonction : mw.ustring.gmatch



En Unicode, on utilisera la fonction : mw.ustring.byte ou la fonction : mw.ustring.codepoint


Cette 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


En Unicode, on utilisera la fonction : mw.ustring.match




string.reverse

[modifier | modifier le wikicode]

Cette 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.


Cette 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


Cette 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.


La 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


mw.ustring.char


Cette 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)

[modifier | modifier le wikicode]

En 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 :
[modifier | modifier le wikicode]

Une 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) :
[modifier | modifier le wikicode]

Un é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) :
[modifier | modifier le wikicode]

Un 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.


Un 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

[modifier | modifier le wikicode]

En 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

[modifier | modifier le wikicode]

Donne la taille maximum autorisée pour un motif (pattern), en octets.


mw.ustring.maxStringLength

[modifier | modifier le wikicode]

Donne la taille maximum autorisée pour une chaîne, en octets.


mw.ustring.byte

[modifier | modifier le wikicode]

mw.ustring.byte( s, i, j )

Retourne les octets d'une chaîne. Identique à string.byte().


mw.ustring.byteoffset

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.ustring.char( ... )

Similaire à string.char(), mais les entiers retournés sont des points de codes Unicode et non des octets.


mw.ustring.codepoint

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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
Panneau d’avertissement 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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.ustring.gmatch( s, pattern )

Similaire à string.gmatch(), mais le motif (pattern) est géré comme décrit dans motifs ustring.


mw.ustring.gsub

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.ustring.isutf8( s )

Retourne vrai si la chaîne est en UTF-8 valide, faux sinon.


mw.ustring.len

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.ustring.rep( s, n )

Identique à string.rep().


mw.ustring.sub

[modifier | modifier le wikicode]

mw.ustring.sub( s, i, j )

Similaire à string.sub(), mais les positions sont en caractères et non en octets.


mw.ustring.toNFC

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

Appelé 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

[modifier | modifier le wikicode]

Les 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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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( 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

[modifier | modifier le wikicode]

mw.text.killMarkers( s )

Retire d'une chaîne toutes les balises spécifiques à MediaWiki.

mw.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

[modifier | modifier le wikicode]

mw.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 '&lt;', '&gt;', '&amp;', '&quot;', et '&nbsp;'. Sinon la liste des entités HTML5 à traiter est chargée depuis la fonction PHP get_html_translation_table.

mw.text.nowiki

[modifier | modifier le wikicode]

mw.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( 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

[modifier | modifier le wikicode]

mw.text.unstripNoWiki( s )

Renvoie la chaine entre balises <nowiki>. Les autres balises restent inchangées.

mw.text.unstrip

[modifier | modifier le wikicode]

mw.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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 7
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Chaînes de caractères
Chap. suiv. :Autres fonctions standards

Exercices :

Sur les fonctions mathématiques
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

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

[modifier | modifier le wikicode]

Nous 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é

[modifier | modifier le wikicode]

Dans 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.


Cette fonction retourne le plus grand de ses paramètres.


Cette fonction retourne le plus petit de ses paramètres.


Cette 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


Cette 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

[modifier | modifier le wikicode]

math.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.

Panneau d’avertissement 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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 8
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Fonctions mathématiques
Chap. suiv. :Gestion de l'environnement

Exercices :

Sur les autres fonctions standards
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

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

[modifier | modifier le wikicode]

Les exemples donnés dans ce paragraphe se trouvent dans le Module:Tables.

La 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.


La 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.


La 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


Retourne 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


Permet 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 | modifier le wikicode]


Les exemples donnés dans ce paragraphe se trouvent dans le Module:Temps.

cette 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 : 1734790301


Cette 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


Cette 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.

Panneau d’avertissement 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.29032
Ce qui signifie que 290.32 millisecondes se sont écoulées depuis le début de l’affichage de cette page.


Cette 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 : Sat Dec 21 14:11:41 2024


Fonctions concernant la librairie Scribunto

[modifier | modifier le wikicode]


Les exemples donnés dans ce paragraphe se trouvent dans le Module:Scribunto


Nous 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


Cette 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

[modifier | modifier le wikicode]

La 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

[modifier | modifier le wikicode]

Cette 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 | modifier le wikicode]



Fonctions concernant le chargement (Librairie Package)

[modifier | modifier le wikicode]


Les exemples donnés dans ce paragraphe se trouvent dans le Module:Package


Cette fonction sera étudiée dans le chapitre sur la gestion de l'environnement.


package.loaders

[modifier | modifier le wikicode]


package.preload

[modifier | modifier le wikicode]


package.seeall

[modifier | modifier le wikicode]




Gestion de l'environnement

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 9
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Autres fonctions standards
Chap. suiv. :Méta-tables

Exercices :

Sur la gestion de l'environnement
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

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

[modifier | modifier le wikicode]

Dans ce paragraphe, nous allons dire quelques mots sur la gestion des erreurs.

Exposé du problème

[modifier | modifier le wikicode]

Lorsqu'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

[modifier | modifier le wikicode]

Comment 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

[modifier | modifier le wikicode]

Une 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

[modifier | modifier le wikicode]

En 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 4.000000000004e-05


{{#invoke:Polynôme|poly2|7}} nous donne : Le résultat est 18678 et le temps d'exécution est 2.000000000002e-05


{{#invoke:Polynôme|poly3|7}} nous donne : Le résultat est 18678 et le temps d'exécution est 0


Nous voyons maintenant clairement que la meilleure des trois fonctions est bien la troisième et la plus mauvaise est bien la première.


Panneau d’avertissement 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

[modifier | modifier le wikicode]

Dans 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

[modifier | modifier le wikicode]

Il 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

[modifier | modifier le wikicode]

C'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

[modifier | modifier le wikicode]

En 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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 10
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Gestion de l'environnement
Chap. suiv. :Librairies Scribunto

Exercices :

Sur les méta-tables
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

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

[modifier | modifier le wikicode]

Si 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

[modifier | modifier le wikicode]

Une 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

[modifier | modifier le wikicode]

Nous 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

[modifier | modifier le wikicode]

Nous 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

[modifier | modifier le wikicode]

Nous 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

[modifier | modifier le wikicode]

Ce 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

[modifier | modifier le wikicode]

Ce 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

[modifier | modifier le wikicode]

Nous 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.


S'agit-il d'un bug dans le Lua ou Scribunto ? Affaire à suivre !


Le champ __concat

[modifier | modifier le wikicode]

Dans 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

[modifier | modifier le wikicode]

Ce 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

[modifier | modifier le wikicode]

La 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

[modifier | modifier le wikicode]

Ce 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).

La 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.

Ce 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.

Ce 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

[modifier | modifier le wikicode]

Ce 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

[modifier | modifier le wikicode]

La 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

[modifier | modifier le wikicode]

Ce 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

[modifier | modifier le wikicode]

Ce 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

[modifier | modifier le wikicode]

Ce 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

[modifier | modifier le wikicode]

Ce 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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 11
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Méta-tables
Chap. suiv. :L'objet Frame

Exercices :

Sur les librairies Scribunto
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

Les librairies Scribunto apporte un ensembles de fonctions spécialisées plus particulièrement adaptés aux projets de la fondation Wikimédia.

Les 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

[modifier | modifier le wikicode]

Toutes 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

[modifier | modifier le wikicode]

mw.allToString

[modifier | modifier le wikicode]

mw.allToString( ... )

Appelle tostring() sur tous ses paramètres, puis concatène les résultats avec une tabulation comme séparateur.

mw.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

[modifier | modifier le wikicode]

mw.getCurrentFrame

[modifier | modifier le wikicode]

mw.getCurrentFrame()

Retourne l'objet frame courant.

mw:getContentLanguage

[modifier | modifier le wikicode]

mw:getLanguage

[modifier | modifier le wikicode]


mw.incrementExpensiveFunctionCount

[modifier | modifier le wikicode]

mw.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()

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( 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() et ipairs() fonctionnent mais les autres méthodes, y compris Initiation 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( object )

Renvoie une représentation lisible par un humain de l’objet object, sous forme de chaîne.

mw.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( 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

[modifier | modifier le wikicode]

La table mw.html

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

Cette 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

On 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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 12
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Librairies Scribunto
Chap. suiv. :L'objet Title

Exercices :

Sur l’objet Frame
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

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

[modifier | modifier le wikicode]

Une 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

[modifier | modifier le wikicode]

Jusqu'à 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

[modifier | modifier le wikicode]

Tous 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

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

Nous 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é :

Panneau d’avertissement Essai avec le modèle Attention


frame:expandTemplate

[modifier | modifier le wikicode]

La 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 :

Panneau d’avertissement Essai avec le modèle Attention

frame:argumentPairs

[modifier | modifier le wikicode]

L'é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

[modifier | modifier le wikicode]

Cette 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-21 14:11:38 et son type est : string

frame:extensionTag

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

Cette fonction permet de récupérer un argument passé en paramètre au module. Il y a 2 signatures possibles :

  1. frame:getArgument(number) - où number représente l'index de l’argument à récupérer
  2. 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

[modifier | modifier le wikicode]
local 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

[modifier | modifier le wikicode]
local 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

[modifier | modifier le wikicode]

Cette 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 | modifier le wikicode]



L'objet Title

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 13
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :L'objet Frame
Chap. suiv. :L'objet URI

Exercices :

Sur l’objet Title
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

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.

La librairie Title

[modifier | modifier le wikicode]

Dans ce paragraphe, nous allons étudier en détail les fonctions se trouvant dans la librairie Title.


Cette 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.


Panneau d’avertissement 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


Panneau d’avertissement 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

[modifier | modifier le wikicode]

mw.title.compare

[modifier | modifier le wikicode]

mw.title.getCurrentTitle

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

Nous 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 :

  • 0 : Espace principal.
  • 1 : Discussion de l'espace principal.
  • 2 : Espace utilisateur.
  • 3 : Espace discussion utilisateur.
  • 4 : Espace Wikiversité.
  • 5 : Espace discussion Wikiversité.
  • 6 : Espace fichier.
  • 7 : Espace discussion fichier.
  • 8 : Espace média wiki.
  • 9 : Espace discussion média wiki.
  • 10 : Espace modèle.
  • 11 : Espace discussion modèle.
  • 12 : Espace aide.
  • 13 : Espace discussion aide.
  • 14 : Espace catégorie.
  • 15 : Espace discussion catégorie.
  • 102 : Espace projet
  • 103 : Espace discussion projet
  • 104 : Espace recherche
  • 105 : Espace discussion recherche
  • 106 : Espace faculté
  • 107 : Espace discussion faculté
  • 108 : Espace département
  • 109 : Espace discussion département
  • 110 : Espace transwiki
  • 111 : Espace discussion transwiki
  • 828 : Espace module
  • 829 : Espace discussion module



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

[modifier | modifier le wikicode]

Tous 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

[modifier | modifier le wikicode]

title.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 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 | modifier le wikicode]


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 | modifier le wikicode]


title.fragment est de type string et donne le fragment (voir w:Identificateur de fragment).


title.subjectPageTitle

[modifier | modifier le wikicode]


identique à mw.title.makeTitle( mw.site.namespaces[title.namespace].subject.id, title.text ).


title.isExternal

[modifier | modifier le wikicode]


indique si la page a un interwiki


title.thePartialUrl

[modifier | modifier le wikicode]


title.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

[modifier | modifier le wikicode]

title.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

[modifier | modifier le wikicode]

indique 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.


C'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

[modifier | modifier le wikicode]

Cette 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 | modifier le wikicode]


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 | modifier le wikicode]


title.isSubpageOf est de type function. title.isSubpageOf(title2) indique si la page est une sous-page de title2.


indique 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

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

hasSubjectNamespace 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 | modifier le wikicode]


title.partialUrl est de type function. title.partialUrl(text) retourne text encodé comme une URL


title.isSubpage

[modifier | modifier le wikicode]

Indique 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

[modifier | modifier le wikicode]

Indique 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

[modifier | modifier le wikicode]

title.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

[modifier | modifier le wikicode]

title.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 est de type string et donne le nom de l'espace de noms de la page concernée.


Indique 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 | modifier le wikicode]


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 | modifier le wikicode]


title.subPageTitle est de type function. subPageTitle(text) est identique à mw.title.makeTitle( title.namespace, title.text .. '/' .. text )


title.inNamespaces

[modifier | modifier le wikicode]


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.


C'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

[modifier | modifier le wikicode]

title.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 | modifier le wikicode]


title.subjectNsText est de type string et donne le nom de l'espace de noms sujet de la page.


title.rootText

[modifier | modifier le wikicode]


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

[modifier | modifier le wikicode]

Indique 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 | modifier le wikicode]


title.rootPageTitle

[modifier | modifier le wikicode]


identique à mw.title.makeTitle( title.namespace, title.rootText )


title.localUrl

[modifier | modifier le wikicode]


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 | modifier le wikicode]


Le type de contenu pour ce titre, sous forme de chaîne. Voir mw:Manual:ContentHandler.


title.protectionLevels

[modifier | modifier le wikicode]


title.interwiki

[modifier | modifier le wikicode]


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

[modifier | modifier le wikicode]

title.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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 14
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :L'objet Title
Chap. suiv. :Librairie HTML

Exercices :

Sur l’objet URI
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.


Nous 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

[modifier | modifier le wikicode]

mw.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( 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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.uri.canonicalUrl( page, query )

Retourne un objet URI for the canonical url for a page, with optional query string/table.


mw.uri.buildQueryString

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.uri.anchorEncode( s )

Encode la chaîne pour être utilisée en tant que fragment MediaWiki.


mw.uri.validate

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.uri.localUrl( page, query )

Retourne un objet URI for the local url for a page, with optional query string/table.


mw.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( 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

[modifier | modifier le wikicode]

Nous 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 :

uri: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.


uri:clone()

Crée une copie de l’objet URI.


uri:extend( parameters )

Intègre la table parameters dans la table de requête de l'objet.




Librairie HTML

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 15
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :L'objet URI
Chap. suiv. :Librairie Language

Exercices :

Sur les librairies Scribunto
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

mw.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

[modifier | modifier le wikicode]

Les 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

[modifier | modifier le wikicode]

mw.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 autofermante
  • args.parent : Parent de l'instance de mw.html actuel (destiné à un usage interne)


Méthodes de la librairie HTML

[modifier | modifier le wikicode]

html:node( builder )

Ajoute un nœud enfant (builder) à l'instance mw.html actuelle.


mw.html:wikitext

[modifier | modifier le wikicode]

html:wikitext( ... )

Ajoute un nombre indéterminé de wikitexte à l’objet mw.html.


mw.html:newline

[modifier | modifier le wikicode]

html:newline()

Ajoute une nouvelle ligne à l’objet mw.html.


html: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


html: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

[modifier | modifier le wikicode]

html: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

[modifier | modifier le wikicode]

html:addClass( class )

Ajoute un nom de classe à l'attribut de classe du noeud.


html: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

[modifier | modifier le wikicode]

html:cssText( css )

Ajoute du code CSS brut à l'attribut du nœud courant.


html: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

[modifier | modifier le wikicode]

html: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

[modifier | modifier le wikicode]

Nous 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.attributes

[modifier | modifier le wikicode]


html.selfClosing

[modifier | modifier le wikicode]




Librairie Language

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 16
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Librairie HTML
Chap. suiv. :Librairie Message

Exercices :

Sur les librairies Scribunto
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

Les 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

[modifier | modifier le wikicode]

Les 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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.language.getFallbacksFor( code )

Renvoie une liste des codes de langue de MediaWiki pour le code spécifié.


mw.language.isSupportedLanguage

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

Nous 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


Étudions maintenant les 22 méthodes dans l’ordre où elles sont sorties :

mw.language:convertGrammar

[modifier | modifier le wikicode]

lang: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 que grammar 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

[modifier | modifier le wikicode]

lang:isRTL()

Retourne vrai si la langue est écrite de droite à gauche, faux si elle est écrite de gauche à droite.


mw.language:getArrow

[modifier | modifier le wikicode]

lang: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

[modifier | modifier le wikicode]

lang: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

[modifier | modifier le wikicode]

lang:getDirMarkEntity( opposite )

Retourne "&lrm;" ou "&rlm;", 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

[modifier | modifier le wikicode]

lang: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

[modifier | modifier le wikicode]

lang:getFallbackLanguages()

Renvoie une liste de codes de langue de MediaWiki pour cet objet langue. Équivalent à mw.language.getFallbacksFor( lang:getCode() ).


mw.language:ucfirst

[modifier | modifier le wikicode]

lang:ucfirst( s )

Convertit le premier caractère de la chaîne en majuscule, comme le fait lang:uc().


mw.language:getDir

[modifier | modifier le wikicode]

lang: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

[modifier | modifier le wikicode]

lang: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 | modifier le wikicode]


mw.language:lc

[modifier | modifier le wikicode]

lang: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 | modifier le wikicode]


mw.language:gender

[modifier | modifier le wikicode]

lang: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

[modifier | modifier le wikicode]

lang:lcfirst( s )

Convertit le premier caractère de la chaîne en minuscule, comme le fait lang:lc().


mw.language:formatNum

[modifier | modifier le wikicode]

lang: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

[modifier | modifier le wikicode]

lang: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

[modifier | modifier le wikicode]

lang:getCode()

Retourne le code de langue correspondant à cet objet.


mw.language:formatDuration

[modifier | modifier le wikicode]

lang: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

[modifier | modifier le wikicode]

lang: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

[modifier | modifier le wikicode]

lang: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

[modifier | modifier le wikicode]

lang: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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 17
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Librairie Language
Chap. suiv. :Librairie Site

Exercices :

Sur les librairies Scribunto
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

Présentation de la librairie message

[modifier | modifier le wikicode]

Cette 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

[modifier | modifier le wikicode]

Les 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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.message.getDefaultLanguage()

Retourne un objet Language pour la langue courante.


Méthodes de la librairie message

[modifier | modifier le wikicode]

Nous 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

[modifier | modifier le wikicode]

msg: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

[modifier | modifier le wikicode]

msg: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

[modifier | modifier le wikicode]

msg: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

[modifier | modifier le wikicode]

msg: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

[modifier | modifier le wikicode]

msg: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

[modifier | modifier le wikicode]

msg: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

[modifier | modifier le wikicode]

msg:exists()

Retourne un booléen indiquant si la clé de message existe.


mw.message:isBlank

[modifier | modifier le wikicode]

msg: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

[modifier | modifier le wikicode]

msg: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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 18
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Librairie Message
Chap. suiv. :Quelques compléments

Exercices :

Sur les librairies Scribunto
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

Présentation de la librairie Site

[modifier | modifier le wikicode]

La librairie Site permet d'obtenir des renseignements sur le projet sur lequel le programme Lua tourne.


Objets de la librairie Site

[modifier | modifier le wikicode]

Les 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 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 :
55331 pages.
16924 articles.
83 fichiers.
948799 éditions.
77071 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

[modifier | modifier le wikicode]

mw.site.stats.usersInGroup( group )

Retourne le nombre d'utilisateurs dans le groupe indiqué.


mw.site.stats.pagesInCategory

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

mw.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.
1908 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

[modifier | modifier le wikicode]

La valeur de $wgSitename.


mw.site.currentVersion

[modifier | modifier le wikicode]

Une chaîne contenant la version actuelle de MediaWiki.


mw.site.interwikiMap

[modifier | modifier le wikicode]

mw.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

[modifier | modifier le wikicode]

Table contenant uniquement les espaces de nom de discussion, indexé par leur numéro. Voir mw.site.namespaces.


mw.site.scriptPath

[modifier | modifier le wikicode]

La valeur de $wgScriptPath.


mw.site.server

[modifier | modifier le wikicode]

La valeur de $wgServer.


mw.site.stylePath

[modifier | modifier le wikicode]

La valeur de $wgStylePath.


mw.site.namespaces

[modifier | modifier le wikicode]

Table 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

[modifier | modifier le wikicode]

Table contenant uniquement les espaces de nom de sujets, indexés par leur numéro. Voir mw.site.namespaces.


mw.site.contentNamespaces

[modifier | modifier le wikicode]

Table contenant uniquement les espaces de nom de contenu, indexés par leur numéro. Voir mw.site.namespaces.




Quelques compléments

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 19
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Librairie Site
Chap. suiv. :Débogage

Exercices :

Sur les compléments
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

Ce chapitre est réservé à tout ce qui n'a pas pu être mis dans les chapitres précédents.

Librairies chargeables

[modifier | modifier le wikicode]

Ces librairies ne sont pas incluses par défaut, mais peuvent être chargées si besoin en utilisant require().

Ceci 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( ... )

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( x )

Retourne le complément binaire de x.


bit32.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( ... )

Équivalent à bit32.band( ... ) ~= 0


bit32.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( 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( 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( 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( 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( 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( 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( 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…


Cette librairie contient des méthodes utiles pour implémenter des librairies Scribunto. Elle peut être chargée avec :

libraryUtil = require( 'libraryUtil' )


libraryUtil.checkType

[modifier | modifier le wikicode]

libraryUtil.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

[modifier | modifier le wikicode]

libraryUtil.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

[modifier | modifier le wikicode]

libraryUtil.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

[modifier | modifier le wikicode]

libraryUtil.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

[modifier | modifier le wikicode]

libraryUtil.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


Les 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.


L'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

Début de la boite de navigation du chapitre
Version imprimable
Icône de la faculté
Chapitre no 20
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Quelques compléments

Exercices :

Sur les compléments
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Version imprimable
Initiation au Lua avec Scribunto/Version imprimable
 », n'a pu être restituée correctement ci-dessus.

Ce chapitre regroupe tous les messages d'erreur et leurs solutions.

attempt to compare number with string

[modifier | modifier le wikicode]

Convertir soit la chaine avec tonumber(), soit le nombre avec tostring().

attempt to index field 'args' (a nil value)

[modifier | modifier le wikicode]

Il 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)

[modifier | modifier le wikicode]

Convertir la table en ajoutant ".text" en suffixe.

bad argument #2 to 'tonumber' (base out of range)

[modifier | modifier le wikicode]

Cela se produit quand il faut convertir un tableau en chaine, ex : tonumber(t)tonumber(t[1]).

Erreur Lua : Cannot pass circular reference to PHP

[modifier | modifier le wikicode]

Se 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

[modifier | modifier le wikicode]

Il manque le return p à la fin du module qui commence par local p = {}.

Modèle en boucle détecté

[modifier | modifier le wikicode]

Un module s’appelle lui-même, il faut donc isoler cette partie récursive du modèle qui l'invoque entre balises <noinclude>...</noinclude>.


GFDL 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.