Bases de Lua. Introduction aux exemples de programmes Lua Lua

Bases de Lua. Introduction aux exemples de programmes Lua Lua

07.09.2021

Bonjour à tous.

Aujourd'hui, nous allons faire un tour rapide du langage Lua, de certaines de ses fonctionnalités, ainsi que de l'exécution de nos scripts dans RakBot.
Lua est un langage de programmation de script conçu pour un traitement rapide des données. Avec l'aide de ce langage, de nombreux développeurs créent de l'intelligence artificielle dans les jeux, écrivent des algorithmes de génération de niveau, et il est également utilisé pour développer des ressources / mods de jeu dans Multi Theft Auto : San Andreas (analogue à SA : MP). En fait, c'est le langage le plus simple et avec l'aide de celui-ci, nous apprendrons à écrire notre propre logique pour les bots que RakBot utilisera.

Passons en revue les bases de la programmation avec lesquelles nous devons travailler.

Remarque : Cet article sera tronqué en termes de langue Lua, car seule une petite partie est utilisée dans RakBot. De nombreuses fonctionnalités Lua manquent tout simplement à RakBot, je vais donc cibler la version RakBot.

C'est une tradition pour tous les auteurs de livres et de documentation en différentes langues, c'est le premier programme à imprimer "Hello World".
Eh bien, essayons de l'écrire, mais en RakBot. Accédez au site Web officiel de RakBot et recherchez la section "Fonctions disponibles", la section "Événements".

Nous avons besoin d'un événement onScriptStart (), qui sont appelés automatiquement lorsque le script est chargé par RakBot lui-même.

Dans cette fonction, nous devons décrire la logique qui écrira "un" Hello World "dans le journal de discussion de RakBot. Pour cela, sur la même page de la documentation, regardons la section" Fonctions ".

Première fonction printLog (texte)- C'est ce dont on a besoin. Avec cette fonction nous enverrons un message au chat RakBot "a. Pour ce faire, nous écrirons :

Nous avons écrit la logique dans une sorte de document texte, mais comment dire à RakBot d'exécuter notre script ? Pour ce faire, vous devez enregistrer le fichier avec l'extension .lua et le mettre dans un dossier scripts, dans le dossier RakBot.
J'ai enregistré un document texte nommé " exemple.lua ". Essayons d'exécuter RakBot et voyons ce que nous obtenons.

Comme on peut le voir, lorsque RakBot démarre, il trouve le script " exemple.lua", puis l'exécute. De là, nous pouvons conclure que le script est initialisé lorsque RakBot lui-même est démarré ou lorsque tous les scripts sont rechargés avec la commande ! recharger les scripts.

Félicitations, vous venez d'écrire votre propre script pour RakBot !

Nous avons déjà appris à écrire Hello World dans la console RakBot, mais nous voulons écrire des bots complexes qui feront tout le travail pour nous, sous certaines conditions.
Presque tout ce qui se passe en programmation peut être décrit comme suit : prendre les données, en faire quelque chose, donner le résultat.
Dans ce cas, les données sont RakBot lui-même. Il lance lui-même nos scripts, ainsi qu'il nous transfère lui-même des données, que nous pouvons traiter à notre guise et obtenir au final le résultat.

Écrivons le script conditionnel le plus simple. La condition sera le surnom du bot. Si le pseudo du bot est "СМaster", alors nous afficherons RakBot " et " CM FOREVER " dans le chat, mais si le pseudo du bot est complètement différent, nous afficherons " Nonamer " dans le chat.
Pour cela, l'opérateur conditionnel if else va nous aider, c'est aussi un opérateur de branche. Il prend une condition qui doit retourner vrai ou faux. Si la condition est vraie, alors le code à l'intérieur sera exécuté, si faux - il ne sera pas exécuté.
La plupart de la logique de toute application est construite sur cela. Littéralement si se traduit par "SI", alors - "SENS", sinon - "AUTRE" Si c'est très difficile - ne vous inquiétez pas, vous comprendrez de plus en plus.

Lua a les opérateurs de comparaison suivants :
> Plus
< Меньше
> = Supérieur ou égal
<= Меньше или равно
~ = Pas égal
== Égal

Si on écrit " CMaster" == "CM"- nous aurons la valeur False, c'est-à-dire faux
Si on écrit " CMaster" == "CMaster"- nous aurons la valeur True, c'est-à-dire vrai.

5> 10 - faux 5< 10 -- истина 10 ~= 15 -- истина 10 >= 5 - vrai

Essayons d'utiliser la logique de branchement dans notre scénario précédent.

Le code que nous avons écrit plus tôt :

Fonction onScriptStart () printLog ("Bonjour tout le monde!"); finir

Transformons comme suit :

Function onScriptStart () botName = getNickName () if (botName == "CMaster") then printLog ("CM FOREVER"); else printLog ("Nonamer"); fin fin

Jetons un coup d'œil à ce code en commençant par le haut. Je vous conseille de commencer à apprendre à lire le code tout de suite. Par conséquent, essayons de lire ce que nous avons.

Fonction onScriptStart () - crée une fonction nommée onScriptStart botName = getNickName () - écris le nom du bot dans la variable botName if (botName == "CMaster") then - si le nom du bot est "CMaster", alors printLog ("CM FOREVER "); - écrivez au chat "CM Forever". else- ELSE, ou si le nom du bot n'est PAS "CMaster" printLog ("Nonamer"); - écrire au chat "Nonamer" fin - fin des conditions fin - fin de la fonction

Essayons de tester le code que nous avons écrit. J'ai enregistré le code modifié sous le nom " exemple.lua "et a lancé RakBot avec le surnom" Mason_Bennett".

Après avoir téléchargé notre script, RakBot a écrit sur le chat de Nonamer. Essayons de nous connecter avec un pseudo " CMaster".

Comme nous pouvons le voir, notre condition fonctionne avec succès et nous voyons ce que nous voulions dans le chat.

Passons un peu en revue les variables. Vous avez un morceau de papier et vous voulez le garder. Enregistrez-le d'une manière ou d'une autre - placez-le quelque part pour ne pas le perdre. Par exemple, nous pouvons mettre notre feuille de papier dans un casier et la sortir quand nous en avons besoin. Si nous avons une nouvelle feuille et que nous n'avons pas besoin de l'ancienne, nous allons jeter l'ancienne feuille et en mettre une nouvelle.
C'est la logique de la variable. Nous pouvons créer une variable avec les noms que nous voulons et y écrire les valeurs, ce que nous avons fait dans l'exemple précédent avec la variable botName.

En Lua, nous pouvons écrire ce que nous voulons dans une variable. Par exemple, je veux créer une variable nommée PaperList et y écrire le texte "Lua - leçon n° 2". Pour cela j'écrirai :

PaperList = "Lua - leçon numéro 1"

Qu'avons-nous fait ici ? Nous avons écrit un nom et utilisé l'opérateur d'affectation "=" et maintenant je peux utiliser cette variable n'importe où dans mon script.
Je pense que si vous vous souvenez des mathématiques au niveau d'un maximum de 5e année, tout sera clair ici.

Lua a plusieurs types de variables, ce sont nil, boolean, number, string. N'ayez crainte, tout est très simple.

En fait, il y en a plusieurs autres, mais j'ai déjà dit que la plupart des fonctionnalités manquaient dans RakBot.

néant - aucune valeur.
boolean - valeurs booléennes, accepte deux variantes de valeurs - true ou false.
nombre est un nombre réel à double précision. Lua n'a pas de type entier, il agit donc à la fois comme un type réel et un type entier.
chaîne est une chaîne, ici, je pense, tout est clair.
Eh bien, essayons de créer des variables et de jouer avec elles.

nombre = 0 ; - créer une variable nommée nombre et affecter la valeur 0
nombre = nombre + 5 ; - en affectant la valeur à la variable numéro + 5 (c'est-à-dire 0 + 5), nous avons maintenant le numéro 5 stocké ici.
nombre ++; - ++ - incrémenter. En d'autres termes, vous prenez une variable et l'incrémentez de un. C'est (5 + 1) - maintenant 6 se trouve dans le nombre variable.
numéro -; - - décrémenter. En d'autres termes, nous le diminuons d'une unité. (6 - 1) - maintenant la valeur est 5.

string = "Hello" - crée une variable de chaîne avec la valeur "Hello"
string = string .. "," - concaténation de chaînes, c'est aussi un ajout de chaîne. Qu'avons-nous fait ici ? Indiquez le nom de la variable, indiquez l'opérateur de concaténation "..", puis indiquez une ligne supplémentaire à ajouter à la première. Nous avons maintenant la valeur "Hello" dans la variable "string".
chaîne = chaîne .. getNickName() - maintenant, à "Bonjour", nous avons ajouté le surnom du bot, que ce soit "Michel". Nous avons maintenant la valeur "Bonjour, Michel" dans la variable de chaîne.

booléen = vrai; - créer une variable booléenne avec la valeur true.
booléen = getNickName() == "Dimosha" - compare le nom du bot avec la chaîne Dimosha. Puisque le nom du bot que nous avons est Michel, de l'exemple précédent, la valeur false sera écrite ici.

Un peu sur les fonctions. Il existe des fonctions qui renvoient des valeurs, et d'autres non. Comme vous l'avez peut-être remarqué, notre fonction onScriptStart ne renvoie pas de valeur, mais exécute simplement le code spécifié à l'intérieur.
Nous pouvons créer nos propres fonctions afin d'isoler une partie de la logique de la méthode et effectuer certaines opérations.
Les fonctions peuvent ou non prendre des valeurs.

Marchons le long du chemin le plus simple : une fonction sans paramètres et sans valeur de retour, qui ajoutera 5 + 10 et affichera le résultat dans la console RakBot. "

Je vais créer une fonction nommée Add :

Fonction Ajouter () - Créer une fonction Ajouter printLog (5 + 10) - utiliser la méthode RakBot pour sortir à la fin de la console - Fin de la fonction

Nous avons créé une fonction pas tout à fait universelle pour deux raisons :
- si je dois ajouter d'autres nombres, je devrai en créer un autre de la même fonction
- Je ne peux pas utiliser la valeur reçue en dehors de la fonction

Essayons de corriger le premier défaut. Pour ce faire, nous allons ajouter deux paramètres à la fonction, qui reprendront les nombres à additionner. Nous le ferons comme suit :

Fonction Add (a, b) printLog (5 + 10) end

Maintenant, dans la méthode, nous avons deux valeurs disponibles, qui sont contenues dans deux nouvelles variables a et b, mais 15 est toujours imprimée sur la console.

Fonction Add (a, b) printLog (a + b) end

Idéalement. Maintenant, lorsque nous appelons cette méthode, nous obtiendrons le résultat de l'ajout dans la console. Essayons de le tester. Modifions notre code en exemple.lua au suivant:

Function Add (a, b) printLog (a + b) end function onScriptStart () Add (5, 10); Ajouter (123, 4324); Ajouter (555, 111) ; finir

Et essayons de lancer RakBot. Voyons ce qui se passe:

Cela a résolu notre premier problème. Essayons de résoudre le second, pour que notre fonction renvoie un résultat.

Réécrivons la fonction Add :

Fonction Add (a, b) return a + b end

return est un mot-clé pour renvoyer une valeur à partir d'une fonction. Réécrivons maintenant la méthode onScriptStart :

Function onScriptStart() printLog ("First value:" ..Add (5, 10)); printLog ("Deuxième valeur :" ..Ajouter (123, 4324)); printLog ("Troisième valeur :" ..Ajouter (555, 111)); finir

Voyons ce qui se passe.

Nous pourrions créer trois variables en attribuant des valeurs à partir de fonctions Ajouter puis les passer à la méthode printLog, mais je ne l'ai pas fait, car le code semble plus lisible et plus agréable.

Maintenant, nous avons appris à créer nos propres fonctions avec des paramètres, sans paramètres et en retournant des valeurs. Je pense que ces bases vous suffiront pour écrire votre propre bot dans le cadre de RakBot "a. Dans les prochaines leçons, nous créerons un bot simple, que nous compliquerons progressivement en ajoutant de plus en plus de nouvelles fonctionnalités et fonctions.

introduction

Ce guide est destiné à ceux qui ont une expérience limitée avec LUA. Nous couvrirons les bases du codage, les blocs de construction pour vous permettre de créer du code plus complexe et fournirons quelques exemples. Le manuel est écrit de manière à ce que vous puissiez l'appliquer immédiatement dans la pratique. Par conséquent, vous devez ouvrir Tabletop Simulator et votre éditeur LUA pour suivre.

Ceci est le premier tutoriel de cette série. La seconde est d'apprendre Lua More. Le troisième est une collection de fonctions utiles appelées Fonctions d'apprentissage Lua.

Avant la première pression de touche

Tout d'abord, je vous recommande fortement d'installer Atom si vous souhaitez créer un script dans Tabletop Simulator. Il sait quelles fonctions peuvent être utilisées et importera/exportera le code vers/depuis TTS.

Ensuite, vous devez ajouter des signets. Vous ferez souvent des liens vers ce site une fois que vous aurez commencé à écrire vos scripts. Vous trouverez ici les fonctionnalités spéciales de Tabletop Simulator et leur fonctionnement. Vous utiliserez le plus souvent des pages API et Object, du moins selon mon expérience.

Préparation

Lorsque vous enregistrez vos scripts sur Tabletop, il utilise votre dernière sauvegarde, puis charge les scripts dedans. Par conséquent, pour tout script que vous avez l'intention d'écrire, vous devrez procéder comme suit :

  • Préparez la table comme vous le souhaitez.
  • Enregistrez le tableau.
  • Chargez le tableau.
Pour cet exercice, prenez une table vide et créez deux objets (j'ai utilisé un bloc carré et un bloc rectangulaire) et un pion rouge.

Appuyez à nouveau parce que vous le ferez bien sûr.

CRÉDIT SUPPLÉMENTAIRE: Lorsque vous créez des tables, il existe plusieurs manières de procéder. La méthode utilisée ici était de fournir une clarté visuelle. Cependant, un moyen de créer des paramètres de bouton comme celui-ci prend beaucoup de place si vous avez beaucoup de boutons. Je préfère concevoir mes tables de manière à économiser de l'espace, mais sans dépasser la bonne marge. En utilisant notre exemple, je créerais une table de paramètres comme celle-ci :

button_parameters = (click_function = "buttonClicked", function_owner = nil, label = "Press Me", position = (0,0.8,0), rotation = (0,0,0), width = 500, height = 500, font_size = 100 )

CRÉDIT SUPPLÉMENTAIRE: C'est le moment idéal pour commencer à jouer avec les différentes choses que vous pouvez faire avec des objets. Accédez à la page Objet dans la base de connaissances et essayez le matériel. Déplacez des objets, faites-les changer de position, changez leurs couleurs, quoi que vous en pensiez.

CRÉDIT SUPPLÉMENTAIRE: De plus, à chaque pression sur le bouton, la fonction click_function est exécutée avec deux paramètres. Le premier est une référence à un objet, en particulier une référence à l'objet auquel le bouton est lié. La seconde est la couleur (par exemple, "Bleu" - bleu) au format ligne, la couleur du joueur qui a appuyé sur le bouton.

5) Affirmation booléenne

Comparaison de variables

De nouveau supprimer tous les scripts à l'intérieur de la fonction buttonClicked (). Nous allons créer une nouvelle variable puis la modifier. La nouvelle variable sera de type booléen. Les valeurs booléennes ne peuvent être que vraies, fausses. Les valeurs booléennes sont toujours écrites en minuscules. Tout d'abord, nous allons créer notre variable sous nos objets et contrôleurs GUID.

vraiOuFalse = vrai

Ensuite, dans buttonClicked, nous allons mettre en place une logique pour vérifier si trueOrFalse est vrai. Si c'est vrai, il imprimera qu'il s'agit de la vérité et le changera en faux. Si vous appuyez à nouveau sur le bouton, il affichera qu'il est False et passera la valeur à True.

si trueOrFalse alors print ("trueOrFalse était vrai.") --trueOrFalse était vrai. trueOrFalse = false else print ("trueOrFalse était false.") --trueOrFalse était false. vraiOuFalse = vraie fin

Nous pourrions également l'écrire comme "if trueOrFalse == true then", mais ceci est facultatif. N'oubliez pas que l'instruction IF doit recevoir une valeur booléenne. Et puisque trueOrFalse est déjà l'un de ceux-là, nous pouvons laisser tomber "== true".

Une boucle est une section de code qui peut s'exécuter plusieurs fois. C'est l'un des éléments les plus complexes que vous utiliserez dans LUA. Ils sont souvent livrés avec des tables, vous permettant d'exécuter du code pour chaque enregistrement de la table.

Ceci est un autre type - ipairs. Les paires sont nécessaires pour les tables sans clés numériques, et les paires i sont nécessaires pour les tables avec clés numériques séquentielles (tableaux). ipairs vient dans l'ordre, où les paires peuvent aller dans n'importe quel ordre.

Lua vous donne le pouvoir ; vous construisez les mécanismes.
// Roberto Ierusalimsky


introduction

Lua est un langage de programmation conçu pour être intégré à d'autres applications afin de permettre à leurs utilisateurs d'écrire des scripts de configuration et des scripts de haut niveau. Lua prend en charge les styles de programmation procédurale, objet et fonctionnelle, mais est également un langage simple. L'interpréteur Lua est écrit en ANSI-C et est une bibliothèque qui peut être connectée à n'importe quel programme. Dans ce cas, le programme de contrôle peut appeler des fonctions de bibliothèque pour exécuter un morceau de code Lua et travailler avec les données définies dans ce code. De plus, le programme de contrôle peut enregistrer ses propres fonctions afin qu'elles puissent être appelées à partir du code Lua. Cette dernière caractéristique permet à Lua d'être utilisé comme un langage qui peut être adapté à un domaine d'utilisation arbitraire. Une autre utilisation de Lua est l'écriture de scripts simples et indépendants. Pour cela, il existe un simple interpréteur Lua qui utilise cette bibliothèque pour exécuter du code entré depuis la console ou depuis un fichier.

Conventions lexicales

Les identificateurs peuvent contenir des lettres, des chiffres et des traits de soulignement et ne peuvent pas commencer par un chiffre.

Les identifiants commençant par un trait de soulignement et contenant uniquement des lettres majuscules sont réservés à un usage interne par l'interpréteur.

Les identificateurs font la distinction entre les lettres majuscules et minuscules.

Les littéraux de chaîne peuvent être placés entre guillemets simples ou doubles. Ils peuvent utiliser les séquences de caractères spéciaux suivantes :

\ n saut de ligne (LF = 0x0a) \ une cloche \ r retour chariot (CR = 0x0d) \ b retour arrière \ t tab \ f saut de formulaire \\ caractère barre oblique inverse \ v tabulation verticale \ " citation \ [ crochet gauche \ " apostrophe \] crochet droit \ ddd caractère avec code ddd (décimal) \ 0 caractère avec code 0

S'il y a une barre oblique inverse à la fin d'une ligne dans le fichier source, la définition d'un littéral de chaîne peut être poursuivie sur la ligne suivante, dans laquelle un caractère de nouvelle ligne est inséré à ce stade.

Les littéraux de chaîne peuvent également être placés entre crochets doubles [[....]]. Dans ce cas, le littéral peut être défini sur plusieurs lignes (les sauts de ligne sont inclus dans le littéral de chaîne) et n'interprète pas les séquences de caractères spéciaux.

S'il y a un saut de ligne immédiatement après les caractères "[[", alors il n'est pas inclus dans le littéral de chaîne.

En plus des doubles crochets, le caractère [=== [....] ===] peut être utilisé comme délimiteur de ligne, dans lequel un nombre arbitraire de signes égaux sont situés entre les crochets répétés (le même pour le délimiteurs d'ouverture et de fermeture).

Les constantes numériques peuvent contenir une partie fractionnaire facultative et un ordre décimal facultatif, spécifiés par les caractères "e" ou "E". Les constantes numériques entières peuvent être spécifiées en hexadécimal à l'aide du préfixe 0x.

Le commentaire commence par "-" (deux caractères moins d'affilée) et se poursuit jusqu'à la fin de la ligne. Si immédiatement après les symboles "-" il y a des symboles "[[", alors le commentaire est multiligne et continue jusqu'aux symboles "]]". Un commentaire multiligne peut contenir des paires de caractères [[....]] imbriquées. En plus des doubles crochets, le caractère [=== [....] ===] peut également être utilisé comme délimiteur pour les commentaires multilignes, dans lesquels un nombre arbitraire de signes égal sont situés entre les crochets répétés (idem pour les délimiteurs d'ouverture et de fermeture). La constante de chaîne échappe aux caractères de début de commentaire.

Si la première ligne du fichier commence par le caractère "#", elle est ignorée. Cela permet à Lua d'être utilisé comme interpréteur de script sur des systèmes de type Unix.

Types de données

Lua fournit les types de données suivants :

Nil vide booléen nombre booléen chaîne numérique chaîne fonction userdata fonction données utilisateur thread table de threads tableau associatif

Le type nil signifie qu'une variable n'a pas de valeur. Ce type a une seule valeur nulle.

Le type booléen a deux significations : vrai et faux.

Une valeur nulle est considérée comme fausse. Toutes les autres valeurs, y compris le nombre 0 et la chaîne vide, sont considérées comme booléennes vraies.

Tous les nombres sont représentés comme des nombres réels à double précision.

Les chaînes sont des tableaux de caractères de 8 bits et peuvent contenir un caractère de code zéro à l'intérieur. Toutes les chaînes de Lua sont constantes, c'est-à-dire vous ne pouvez pas modifier le contenu d'une chaîne existante.

Les fonctions peuvent être affectées à des variables, transmises aux fonctions en tant qu'argument, renvoyées comme résultat d'une fonction et stockées dans des tables.

Le type userdata correspond à un pointeur non typé sur lequel des données arbitraires peuvent être localisées. Un programme Lua ne peut pas travailler directement avec de telles données (les créer, les modifier). Ce type de données ne correspond à aucune opération prédéfinie autre que l'affectation et la comparaison pour l'égalité. En même temps, de telles opérations peuvent être définies en utilisant le mécanisme des méta-méthodes.

Le type de thread correspond à un thread exécutable indépendamment. Ce type de données est utilisé par le moteur de coroutine.

Le type de table correspond aux tables - tableaux associatifs pouvant être indexés par n'importe quelle valeur et pouvant contenir simultanément des valeurs de types arbitraires.

Le type de l'objet stocké dans la variable peut être trouvé en appelant la fonction type (). Cette fonction retourne une chaîne contenant le nom canonique du type : "nil", "number", "string", "boolean", "table", "function", "thread", "userdata".

Les conversions entre les nombres et les chaînes se produisent automatiquement lorsqu'elles sont utilisées dans le contexte approprié.

Les opérations arithmétiques impliquent des arguments numériques, et essayer de le faire sur des chaînes les convertira en nombres. Les opérations de chaîne effectuées sur des nombres conduisent à leur conversion en chaîne à l'aide d'une conversion de format fixe.

Vous pouvez également convertir explicitement un objet en chaîne à l'aide de la fonction tostring () ou en nombre à l'aide de la fonction tonumber (). Pour plus de contrôle sur la conversion des nombres en chaînes, utilisez la fonction de conversion de format.

Variables

En Lua, les variables n'ont pas besoin d'être déclarées. La variable apparaît lors de sa première utilisation. Si une variable est utilisée qui n'a pas été précédemment initialisée, alors elle est nulle. Les variables n'ont pas de type statique, le type d'une variable est déterminé par sa valeur actuelle.

Une variable est considérée comme globale si elle n'est pas explicitement déclarée comme locale. Les déclarations de variables locales peuvent être situées n'importe où dans le bloc et peuvent être combinées avec leur initialisation :

Local x, y, z local a, b, c = 1, 2, 3 local x = x

Lorsqu'une variable locale est initialisée à droite du signe égal, la variable d'entrée n'est pas encore disponible et la valeur de la variable externe au bloc courant est utilisée. C'est pourquoi l'exemple de la troisième ligne est correct (il démontre un idiome couramment utilisé de la langue).

Pour les variables locales, l'interpréteur utilise des portées lexicales, c'est-à-dire la portée d'une variable s'étend du point de sa déclaration (première utilisation) à la fin du bloc courant. Dans ce cas, la variable locale est visible dans les blocs internes au bloc dans lequel elle est décrite. La variable locale disparaît lorsqu'elle sort de la portée. Si une variable locale est définie en dehors du bloc, alors une telle variable disparaît à la fin de l'exécution de ce morceau de code, puisque le morceau de code est exécuté par l'interpréteur comme une fonction sans nom. Une variable globale initialisée existe pendant toute la durée de l'opération de l'interpréteur.

Pour supprimer une variable, vous pouvez simplement lui affecter la valeur nil.

Les tableaux, les fonctions et les données utilisateur sont des objets. Tous les objets sont anonymes et ne peuvent pas être la valeur d'une variable.

Les variables stockent les références aux objets. Lors de l'affectation, du passage à une fonction en tant qu'argument et du retour d'une fonction en conséquence, les objets ne sont pas copiés, seules les références à eux sont copiées.

les tables

Les tables (type table) correspondent à des tableaux associatifs, qui peuvent être indexés par toutes valeurs autres que nil et qui peuvent simultanément contenir des valeurs de types arbitraires autres que nil. Les éléments de table peuvent également être indexés par des objets - tables, fonctions et objets de données utilisateur. Les éléments de tableau auxquels aucune valeur n'a été affectée sont nuls par défaut.

Les tableaux sont la principale structure de données en Lua. Ils représentent également des structures, des classes et des objets. Dans ce cas, l'indexation par le nom de chaîne du champ de structure est utilisée. Étant donné qu'un élément de tableau peut être une fonction, les méthodes sont également autorisées dans les structures.

Les tableaux sont indexés à l'aide de crochets : array. L'entrée struct.field est équivalente à l'entrée suivante : struct ["field"]. Cette fonctionnalité syntaxique permet aux tables d'être utilisées comme enregistrements de champs nommés.

Un constructeur de table est une expression qui crée et renvoie une nouvelle table. Chaque exécution du constructeur crée une nouvelle table. Le constructeur de table est une liste d'initialiseurs de champs (éventuellement vides) entourés d'accolades, séparés par une virgule ou ";" (point virgule). Les options suivantes sont valides pour les initialiseurs de champ :

table exp2 = nom exp2 = table exp ["nom"] = table exp exp [j] = exp

Dans ce dernier cas, la variable j parcourt des valeurs entières consécutives, à partir de 1. Les deux premiers initialiseurs ne modifient pas la valeur de ce compteur. Voici un exemple de construction d'un tableau :

X = (long = 12, 11, 12, = 1123)

Après avoir exécuté un tel opérateur, les champs de la table recevront les valeurs suivantes :

X ["len"] = x.len = 12 x = 11 x = 12 x = 1123

Si le dernier élément de la liste d'initialisation est un appel de fonction, les valeurs renvoyées par la fonction sont placées séquentiellement dans la liste d'initialisation. Ce comportement peut être modifié en mettant l'appel de fonction entre parenthèses. Dans ce cas, de toutes les valeurs renvoyées par la fonction, seule la première est utilisée.

Le dernier initialiseur peut être suivi d'un caractère de séparation d'initialisation de champ facultatif (virgule ou point-virgule).

Opérations

Les opérations de base sont listées ci-dessous :

Changer de signe + - * / arithmétique ^ exponentiation == ~ = égalité< <= >> = ordre non et ou logique .. concaténation de chaînes # obtenir la longueur de la chaîne ou du tableau

Lors de l'utilisation d'opérations arithmétiques, les chaînes qui ont une valeur numérique y sont converties. Lors de la concaténation de valeurs numériques, elles sont automatiquement converties en chaînes.

La comparaison d'égalité n'effectue pas de conversion de type. Les objets de types différents sont toujours considérés comme différents.

Par conséquent, "0" ~ = 0, et lors de l'indexation, a et a ["0"] correspondent à des cellules différentes du tableau. Lors de la comparaison pour l'égalité / l'inégalité des objets, la comparaison des références aux objets est effectuée. Les variables se référant au même objet sont égales.

Lors de la détermination de l'ordre, les types des arguments doivent être les mêmes, c'est-à-dire les nombres sont comparés aux nombres et les chaînes sont comparées aux chaînes.

Les relations d'égalité et d'ordre se traduisent toujours par vrai ou faux, c'est-à-dire valeur booléenne.

Dans les opérations logiques, nil est traité comme faux et toutes les autres valeurs, y compris zéro et la chaîne vide, sont traitées comme vraies.

Lors du calcul de la valeur, un court-circuit est utilisé - le deuxième argument n'est calculé que si nécessaire.

Le tableau suivant des priorités et de l'associativité des opérations s'applique :

^ pas # - (unaire) * / + -< > <= >= ~ = == .. et ou

Opérations logiques et idiomes associés

L'opérateur not renvoie toujours une valeur booléenne, acceptant un argument de type arbitraire (dans ce cas, seule la valeur nil correspond à la valeur booléenne false, le reste est traité comme vrai). En revanche, les opérateurs et et ou renvoient toujours l'un de leurs arguments. L'opérateur ou renvoie son premier argument si sa valeur est différente de false et nil, et son deuxième argument sinon. L'opérateur and renvoie son premier argument si sa valeur est false ou nil et son deuxième argument sinon. Ce comportement est basé sur le fait que toutes les valeurs non nulles sont traitées comme vraies.

Plusieurs idiomes courants sont associés à ce comportement. Le tableau suivant montre l'opération idiomatique à gauche et la notation normale équivalente à droite :

X = x ou v si x == nul alors x = v fin x = (e et a) ou b si e ~ = nul alors x = a sinon x = b fin

Le premier idiome est souvent utilisé pour attribuer une valeur par défaut à une variable non initialisée. Le second idiome est équivalent à l'opérateur C" x = e? A, b (ici la valeur de la variable a est supposée non nulle).

Les opérateurs

Lua n'a pas de fonction dédiée pour démarrer l'exécution du programme. L'interpréteur exécute séquentiellement les instructions qu'il reçoit d'un fichier ou d'un programme de contrôle. Ce faisant, il précompile le programme en une représentation binaire, qui peut également être sauvegardée. Tout bloc de code est exécuté en tant que fonction anonyme, de sorte que des variables locales peuvent y être définies et une valeur peut en être renvoyée.

Les opérateurs peuvent (mais pas nécessairement) être séparés par ";" ...

Les affectations multiples sont autorisées :

Var1, var2 = val1, val2

Dans ce cas, l'alignement est effectué - les valeurs supplémentaires sont supprimées et les variables correspondant aux valeurs manquantes se voient attribuer la valeur nulle. Toutes les expressions du côté droit d'une affectation multiple sont évaluées avant l'affectation elle-même.

Si la fonction renvoie plusieurs valeurs, alors en utilisant plusieurs affectations, vous pouvez obtenir les valeurs de retour :

X, y, z = f (); a, b, c, d = 5, f ();

En général, si un appel de fonction se trouve à la fin de la liste de valeurs à droite du signe d'affectation, alors toutes les valeurs renvoyées par la fonction sont ajoutées à la fin de la liste de valeurs. Ce comportement peut être modifié en mettant l'appel de fonction entre parenthèses. Dans ce cas, de toutes les valeurs renvoyées par la fonction, seule la première est utilisée.

Les principaux opérateurs sont listés ci-dessous :

Faire ... terminer si ... alors ... terminer si ... alors ... sinon ... terminer si ... alors ... sinon si ... alors ... terminer si ... alors. .. elseif ... then ... else ... end while ... do ... end repeat ... until ... for var = start, stop do ... end for var = start, stop, étape faire ... fin retour retour ... pause

Le bloc do ... end transforme une séquence d'instructions en une seule instruction et ouvre une nouvelle portée dans laquelle vous pouvez définir des variables locales.

Dans les instructions if, while et repeat, toutes les valeurs d'expression autres que false et nil sont interprétées comme vraies.

Voici la forme générale de l'instruction if :

Si ... alors ... (sinon ... alors ...) fin

L'instruction return peut ne contenir aucune valeur de retour ou contenir une ou plusieurs expressions (liste). Puisqu'un bloc de code est exécuté en tant que fonction anonyme, la valeur de retour peut être non seulement pour la fonction, mais également pour un bloc de code arbitraire.

Les instructions return et break doivent être les dernières instructions du bloc (c'est-à-dire qu'elles doivent être soit les dernières instructions du bloc de code, soit placées immédiatement avant les mots end, else, elseif, until). À l'intérieur du bloc, vous devez utiliser l'idiome do return end ou do break end.

L'instruction for est exécutée pour toutes les valeurs de la variable de boucle de la valeur de début à la valeur de fin, inclus. La troisième valeur, si elle est spécifiée, est utilisée comme étape de modification de la variable de boucle. Toutes ces valeurs doivent être numériques. Ils ne sont évalués qu'une seule fois avant d'exécuter la boucle. La variable de boucle est locale à cette boucle et n'est pas disponible en dehors de son corps. La valeur d'une variable de boucle ne peut pas être modifiée à l'intérieur du corps de la boucle. Voici le pseudocode qui montre l'exécution de l'instruction for :

Do local var, _limit, _step = tonumber (start), tonumber (stop), tonumber (step) sinon (var et _limit et _step) alors error () end while (_step> 0 et var<=_limit) or (_step<=0 and var>= _limit) do ... var = var + _step end end

Les fonctions

Une définition de fonction est une expression exécutable (constructeur de fonction) qui s'évalue en un objet de type fonction :

F = fonction (...) ... fin

La liste des arguments de fonction (éventuellement vides) est placée entre parenthèses. La liste des arguments de la fonction peut se terminer par des points de suspension - dans ce cas, la fonction a un nombre variable d'arguments. Le corps de la fonction est placé entre la parenthèse fermante et l'instruction de fin.

Les formulaires abrégés suivants sont disponibles pour définir une fonction :

Function fname (...) ... end fname = function (...) ... end local function fname (...) ... end local fname = function (...) ... end function x .fname (...) ... end x.fname = fonction (...) ... end function x: fname (...) ... end x.fname = function (self, ...) ... finir

Au moment de l'exécution du constructeur de fonction, il construit également fermeture- un tableau de toutes les variables locales disponibles dans la fonction et externes à celle-ci. Si une fonction est passée comme valeur de retour, alors elle conserve l'accès à toutes les variables incluses dans sa fermeture. Chaque fois que le constructeur de fonction est exécuté, une nouvelle fermeture est construite.

Un appel de fonction se compose d'une référence de fonction et d'une liste d'arguments entre parenthèses (éventuellement vide). Une référence de fonction peut être n'importe quelle expression qui évalue une fonction. Il ne peut pas y avoir de saut de ligne entre la référence de fonction et la parenthèse ouvrante.

Les formes abrégées suivantes sont disponibles pour appeler des fonctions :

F (...) f ((...)) f ("...") f "..." f ("") f "" f ([[...]]) f [[. ..]] x : f (...) xf (x, ...)

Dans le premier cas, le seul argument est une table à la volée, et dans les trois suivants, il s'agit d'un littéral de chaîne. Dans ce dernier cas, x est utilisé comme table à partir de laquelle la variable f est récupérée, qui est une référence à la fonction appelée, et la même table est passée à la fonction comme premier argument. Cette fonctionnalité syntaxique est utilisée pour implémenter des objets.

Lorsqu'une fonction est appelée, les valeurs de types simples sont copiées dans les arguments par valeur, et pour les objets, les références sont copiées dans les arguments. Lorsque la fonction est appelée, le nombre d'arguments est aligné - les valeurs supplémentaires sont ignorées et les arguments correspondant aux valeurs manquantes sont nuls. Si à la fin de la liste des paramètres, il y a un appel à une fonction qui renvoie plusieurs valeurs, alors toutes sont ajoutées à la liste des arguments. Ce comportement peut être modifié en mettant l'appel de fonction entre parenthèses. Dans ce cas, de toutes les valeurs renvoyées par la fonction, seule la première est utilisée.

Si la fonction a un nombre variable d'arguments, alors les valeurs qui ne sont affectées à aucun des arguments peuvent être obtenues en utilisant un nom spécifique .... Ce nom se comporte comme un ensemble de valeurs renvoyé par une fonction c'est-à-dire lorsqu'il apparaît à l'intérieur d'une expression ou au milieu d'une liste de valeurs, seule la première valeur de retour est utilisée. Si le nom ... apparaît en dernière position de la liste de valeurs (dans les constructeurs de table, dans les affectations multiples, dans la liste d'arguments lors de l'appel d'une fonction et dans l'instruction return), alors toutes les valeurs renvoyées sont placé à la fin de cette liste (règle de complétion de liste). Pour supprimer ce comportement, le nom ... peut être placé entre parenthèses. Vous pouvez transformer un ensemble de valeurs en liste en utilisant l'idiome (...).

Si la fonction prend un seul argument et le traite comme une table, dont les éléments sont indexés par les noms des paramètres formels de la fonction, alors dans ce cas l'appel au mécanisme des arguments nommés est réellement implémenté :

Fonction rename (arg) arg.new = arg.new ou arg.old .. ".bak" return os.rename (arg.old, arg.new) end rename (old = "asd.qwe")

Le retour de la fonction se produit à la fois lorsque l'exécution de son corps est terminée et lorsque l'instruction return est exécutée. L'instruction return peut renvoyer une ou plusieurs valeurs. Si à la fin de la liste des valeurs de retour, il y a un appel à une fonction qui renvoie plusieurs valeurs, alors toutes sont ajoutées à la liste des arguments. Ce comportement peut être modifié en mettant l'appel de fonction entre parenthèses. Dans ce cas, de toutes les valeurs renvoyées par la fonction, seule la première est utilisée.

Si la fonction est appelée en tant qu'instruction, toutes les valeurs de retour sont détruites. Si la fonction est appelée depuis une expression ou depuis le milieu d'une liste de valeurs, seule la première valeur de retour est utilisée. Si la fonction est appelée à partir de la dernière position de la liste de valeurs (dans les constructeurs de table, avec plusieurs affectations, dans la liste d'arguments lors de l'appel de la fonction et dans l'instruction return), alors toutes les valeurs renvoyées sont placées à la fin de cette liste (la règle pour compléter les listes). Pour supprimer ce comportement, un appel de fonction peut être placé entre parenthèses.

Il existe une fonction prédéfinie unpack() qui prend un tableau dont les éléments sont indexés à partir de 1 et renvoie tous ses éléments. Cette fonction peut être utilisée pour appeler des fonctions qui prennent un nombre variable d'arguments, générant dynamiquement une liste d'arguments réels. L'opération inverse s'effectue de la manière suivante :

List1 = (f ()) - crée une liste de toutes les valeurs renvoyées par la fonction f () list2 = (...) - crée une liste de toutes les valeurs passées à la fonction avec un nombre variable d'arguments

Le nombre variable de valeurs de retour, la règle de remplissage des listes et la possibilité de passer à une fonction, tous les paramètres formels ne peuvent pas interagir de manière non triviale. Souvent, une fonction (comme foo()) renverra une réponse en cas d'achèvement normal et nil et un message d'erreur en cas d'achèvement anormal. La fonction assert (val, msg) génère une erreur avec le message message (en appelant la fonction error (msg)) si val est faux ou nil et renvoie val sinon. Ensuite, l'opérateur

V = affirmer (foo (), "message")

en cas de succès, attribue la valeur renvoyée par foo () à v. Dans ce cas, foo() renvoie une seule valeur et assert() reçoit un paramètre msg nil. En cas d'erreur, la fonction assert() obtient nil et un message d'erreur.

Itérateurs

Les itérateurs sont utilisés pour énumérer les éléments de séquences arbitraires :

Pour v_1, v_2, ..., v_n dans explist do ... end

Le nombre de variables dans la liste v_1, ..., v_n peut être arbitraire et ne doit pas nécessairement correspondre au nombre d'expressions dans la liste explist. L'exlist est généralement un appel à la fonction de fabrique d'itérateurs. Une telle fonction renvoie une fonction d'itération, l'état et la valeur initiale de la variable de contrôle de boucle. L'itérateur est interprété comme suit :

Do local f, s, v_1 ​​​​= explist local v_2, ..., v_n while true do v_1, ..., v_n = f (s, v_1) if v_1 == nil then break end ... end end

A chaque étape, les valeurs de toutes les variables v_k sont calculées en appelant la fonction itérateur. La valeur de la variable de contrôle v_1 contrôle la fin de la boucle - la boucle se termine dès que la fonction d'itération renvoie nil comme valeur pour la variable var_1.

En fait, la variable v_1 contrôle les itérations, et le reste des variables peut être considéré comme la "queue" renvoyée par la fonction itérateur :

Do local f, s, v = explist alors que true do v = f (s, v) if v == nil then break end ... end end

L'opérateur dans lequel la fonction d'usine est appelée est interprété comme un opérateur d'affectation normal, c'est-à-dire une fonction d'usine peut renvoyer n'importe quel nombre de valeurs.

Itérateurs sans état

Un itérateur sans état ne stocke aucune information interne lui permettant de déterminer sa position dans le conteneur itérable. La valeur suivante de la variable de contrôle est calculée directement à partir de sa valeur précédente et l'état est utilisé pour stocker une référence au conteneur itérable. Voici un exemple d'itérateur simple et sans état :

Fonction iter (a, i) i = i + 1 local v = a [i] if v then return i, v else return nil end end function ipairs (a) return iter, a, 0 end

Itérateurs stockant l'état dans une fermeture

Si un itérateur a besoin d'un état interne pour parcourir un conteneur, le moyen le plus simple est de le stocker dans une fermeture créée à partir du contexte de la fonction d'usine. Voici un exemple simple :

Fonction ipairs (a) local i = 0 local t = a local function iter () i = i + 1 local v = t [i] if v then return i, v else return nil end end return iter end

Ici, l'itérateur stocke l'ensemble du contexte dans une fermeture et n'a pas besoin de l'état et de la valeur actuelle de la variable manipulée. En conséquence, l'itérateur n'accepte pas la variable d'état et de contrôle, et l'usine ne renvoie pas la valeur d'état et la valeur de départ de la variable de contrôle.

Itérateurs standards

L'utilisation la plus courante des itérateurs est de parcourir des éléments de table. Il existe plusieurs fonctions de fabrique d'itérateurs prédéfinies pour cela. La fabrique de paires (t) renvoie un itérateur donnant à chaque pas un indice dans la table et une valeur allouée à cet indice :

Pour idx, val par paires (tbl) do ... end

En fait, cet itérateur est facile à définir à l'aide de la fonction standard next (tbl, idx) :

Les paires de fonctions (tbl) retournent next, tbl, nil end

La fonction next (tbl, idx) renvoie la valeur d'index suivante après idx après un certain parcours de la table tbl (l'appel de next (tbl, nil) renvoie la valeur d'index initiale ; nil est renvoyé lorsque les éléments de la table sont épuisés).

La fabrique ipairs (tbl) renvoie un itérateur qui fonctionne exactement comme celui décrit ci-dessus, mais pour parcourir des tables indexées avec des entiers commençant à 1.

Métatables

Chaque table et objet du type userdata peut avoir une méta-table - une table régulière, dont les champs déterminent le comportement de l'objet d'origine lorsque certaines opérations spéciales lui sont appliquées. Par exemple, lorsqu'un objet s'avère être un opérande en plus, l'interpréteur recherche dans la méta-table un champ nommé __add et, si un tel champ est présent, utilise alors sa valeur comme une fonction qui effectue l'ajout. Les méta-tables vous permettent de définir le comportement d'un objet lors d'opérations arithmétiques, de comparaisons, de concaténation et d'indexation. Vous pouvez également définir une fonction à appeler lorsqu'un objet de type userdata est libéré. Les indices (noms de champs) de la méta table sont appelés événements et les valeurs correspondantes (gestionnaires d'événements) sont métaméthodes.

Par défaut, la table nouvellement créée n'a pas de table méta. Toute table mt peut être transformée en méta-table de la table t en appelant la fonction setmetatable (t, mt). La fonction getmetatable (t) renvoie la méta table de la table t ou nil si la table n'a pas de méta table. N'importe quelle table peut agir comme une méta-table pour n'importe quelle autre table, y compris elle-même.

Lua définit les événements suivants :

Add, __sub, __mul, __div opérations arithmétiques __pow exponentiation __unm unaire moins __concat concaténation __eq, __lt, __le opérations de comparaison __index accès par index manquant __newindex affectation à un nouvel élément de table __call appel de table à la fonction __tostring

L'expression a ~ = b est évaluée comme non (a == b). L'expression a> b s'évalue comme b< a . Выражение a >= b est calculé comme b<= a . При отсутствии метаметода __le операция <= вычисляется как not (b < a) т.е. с помощью метаметода __lt .

Pour les opérations binaires, le gestionnaire est sélectionné comme suit : le premier opérande est interrogé et, s'il ne définit pas de gestionnaire, le deuxième opérande est interrogé. Pour les opérations de comparaison, une métaméthode est sélectionnée uniquement si les opérandes comparés sont du même type et de la même métaméthode pour effectuer cette opération. Le manuel d'utilisation fournit un pseudo-code en Lua qui illustre le contexte de l'appel de métaméthodes.

Le gestionnaire d'événements __index peut être une fonction ou une table. Dans le cas d'une fonction, le gestionnaire est appelé et la table et la valeur d'index lui sont transmises. Une telle fonction doit renvoyer le résultat de l'indexation. Dans le cas d'une table, la table est réindexée avec le même index. Si le gestionnaire d'événements __newindex est présent, il est appelé au lieu d'affecter une valeur à un nouvel élément de table. Si ce gestionnaire est une table, alors l'affectation est faite à cette table.

La métaméthode __tostring permet de gérer la conversion d'un objet (table ou userdata) en chaîne. La méthode meta __metatable vous permet de gérer l'opération get meta table. Si ce champ dans la méta-table a une valeur, alors la fonction getmetatable() renverra la valeur de ce champ, et la fonction setmetatable() échouera.

Exemples d'utilisation de méta-tables

Valeur par défaut des champs de table

L'exemple suivant utilise des méta-tables pour attribuer une valeur par défaut aux éléments de table manquants :

Fonction set_def (t, v) local mt = (__index = function () return v end) setmetatable (t, mt) end

Voici une solution plus simple qui n'utilise pas de méta-table distincte pour chaque valeur par défaut :

Clé locale = () local mt = (__index = fonction (t) return t end) function set_def (t, v) t = v setmetatable (t, mt) end

Ici, la table de clés locale (vide) est utilisée comme un index unique connu auquel la valeur par défaut v est stockée dans la table d'origine t. Toutes les tables pour lesquelles la valeur par défaut des champs manquants est définie partagent une méta-table commune mt. La méta-méthode __index de cette méta-table intercepte les appels aux champs de table manquants et renvoie la valeur stockée dans la table elle-même à l'index de clé.

Cette solution présente un inconvénient : une nouvelle paire clé-valeur apparaît dans le tableau, qui apparaîtra lorsque vous tenterez de parcourir tous les éléments du tableau.

Tableau proxy

Dans l'exemple suivant, une table vide agit comme un proxy pour le transfert des appels vers les champs de la table :

Clé locale = () local mt = (__index = fonction (t, k) return t [k] fin, __newindex = fonction (t, k, v) t [k] = v fin) fonction proxy (t) local proxy = () proxy = t setmetatable (proxy, mt) renvoie la fin du proxy

Ici, la table de clés locale (vide) est utilisée comme un index délibérément unique par lequel une référence à la table d'origine t est stockée dans la table proxy. Un proxy est une table dont le seul élément a l'index de clé, donc accéder à n'importe quel élément du proxy entraînera un appel à la méta-méthode. La méta-table commune à tous les proxys définit les méta-méthodes __index et __newindex, qui récupèrent la table d'origine à partir d'un seul élément proxy, en l'indexant avec la table clé.

Les méta-méthodes peuvent fournir une discipline arbitraire de traitement des appels aux champs de la table source. Des exemples simples sont la consignation des hits ou la génération d'une erreur lors de la tentative de modification de la valeur d'un élément de table.

Tableaux faibles

Si un objet a été utilisé comme index sur une table, ou si une référence à celui-ci a été stockée dans la table, le ramasse-miettes ne pourra pas récupérer l'objet. Dans le même temps, dans certains cas, il est souhaitable d'avoir une table dans laquelle la relation de ses éléments avec les clés et/ou les valeurs est "faible", c'est-à-dire ne pas interférer avec la collecte des ordures. Typiquement, un tel besoin survient lors de la mise en cache des résultats des calculs dans une table et du stockage des attributs d'objet dans une table indexée par les objets eux-mêmes. Dans le premier cas, un maillon faible est souhaitable pour les valeurs, dans le second, pour les indices.

La relation des éléments de la table avec les objets (valeurs et indices) est déterminée par la valeur du champ de chaîne __mode de sa méta-table. Si ce champ contient le caractère "k", alors le lien pour les indices (clés) est faible ; s'il contient le caractère "v", alors le lien pour les valeurs est rendu faible. Le champ peut contenir les deux caractères, ce qui crée un couplage lâche pour les indices et les valeurs.

Si nous utilisons des tables faibles, alors pour le problème ci-dessus de faire correspondre la table de la valeur par défaut pour les champs manquants, la solution suivante peut être donnée :

Local defaults = () setmetatable (defaults, (__mode = "k")) local mt = (__index = function (t) return defaults [t] end) function set_def (t, d) defaults [t] = d setmetatable (t , mt) fin

Voici une autre solution, où les tables faibles stockent les méta-tables, dont le nombre est le même que le nombre de valeurs par défaut différentes :

Local metas = () setmetatable (metas, (__mode = "v")) function set_def (t, d) local mt = metas [d] if mt == nil then mt = (__index = function () return d end) metas [d] = mt end setmettable (t, mt) end

Contexte mondial

Toutes les variables globales sont des champs d'une table régulière appelée contexte mondial... Cette table est accessible via la variable globale _G. Puisque toutes les variables globales sont des champs de contexte, _G._G == _G.

Le contexte global permet d'accéder aux variables globales par un nom généré dynamiquement :

Val = _G _G = val

Le contexte global étant une table régulière, une méta table peut lui correspondre. L'exemple suivant injecte des variables d'environnement dans la portée globale en tant que variables globales en lecture seule :

Local f = fonction (t, i) return os.getenv (i) end setmetatable (_G, (__index = f))

La même technique permet de refuser l'accès aux variables globales non initialisées.

Paquets

Les packages sont le principal moyen de définir un ensemble de fonctionnalités associées sans polluer la portée globale. En règle générale, un package est un fichier distinct qui, dans la portée globale, définit une seule table contenant toutes les fonctions de ce package :

Mon_paquet = () fonction mon_paquet.foo () ... fin

Vous pouvez également rendre toutes les fonctions locales et former séparément un tableau des fonctions exportées :

Fonction locale foo () ... end local function bar () ... end my_package = (foo = foo, bar = bar,)

Le package est chargé à l'aide de la fonction require(), et lors du chargement, le nom passé à cette fonction (il ne peut pas contenir d'extension ajoutée automatiquement) est disponible via la variable _REQUIREDNAME :

Si _REQUIREDNAME == nil alors run_some_internal_tests () se termine

Classes et objets

La construction tbl: func () (lors de la déclaration d'une fonction et lors de son appel) fournit la fonctionnalité de base qui vous permet de travailler avec une table en tant qu'objet. Le principal problème est de générer de nombreux objets avec un comportement similaire, c'est-à-dire issus d'une classe :

Classe de fonction () cl = () cl .__ index = cl - cl sera utilisé comme méta-table return cl end function object (cl, obj) obj = obj ou () - il peut déjà y avoir des champs remplis setmetatable (obj , cl ) return obj end

Ici, la fonction de classe crée une table vide, préparée pour devenir la méta-table de l'objet. Les méthodes de classe sont créées par les champs de cette table, c'est-à-dire une classe est une table qui contient à la fois les méthodes de l'objet et ses méta-méthodes. La fonction object() crée un objet de la classe spécifiée - une table qui a la classe spécifiée comme méta-table. Le deuxième argument peut être une table contenant les champs initialisés de l'objet.

Some_Class = classe () fonction Some_Class: foo () ... end function Some_Class: new () return object (self, (xxx = 12)) end x = Some_Class: new () x: foo ()

Héritage

Dans l'implémentation décrite, la méta-table de la classe est laissée inutilisée, ce qui facilite l'implémentation de l'héritage. La classe héritante est créée en tant qu'objet de la classe, après quoi elle définit le champ __index de telle sorte qu'il puisse être utilisé comme méta-table :

Sous-classe de fonction (pcl) cl = pcl: new () - instancie cl .__ index = cl - et en fait une classe return cl end

Vous pouvez maintenant ajouter de nouveaux champs et méthodes à la classe héritée résultante :

Der_Class = sous-classe (Some_Class) fonction Der_Class : new () local obj = objet (self, Some_Class : new ()) obj.yyy = 13 - ajouter de nouveaux champs return obj end function Der_Class : bar () ... end - et nouveau méthodes y = Der_Class : new () y : foo () y : bar ()

Le seul point non trivial ici est d'utiliser la fonction new() de la classe ancêtre puis de remplacer la méta table en appelant la fonction object().

Lors de l'accès aux méthodes de l'objet de la classe héritante, elles sont tout d'abord recherchées dans la méta-table, c'est-à-dire dans la classe héritière elle-même. Si la méthode a été héritée, alors cette recherche sera infructueuse et la méta-table de la classe héritante sera accessible, c'est-à-dire à la classe des ancêtres.

Le principal inconvénient de la solution générale ci-dessus est l'impossibilité de passer des paramètres à la fonction new () de la classe ancêtre.

Arguments de ligne de commande

Les arguments de ligne de commande transmis au démarrage sont disponibles en tant qu'éléments du tableau arg.

Scripts Lua

Un script écrit en Lua n'a pas de fonction spéciale à partir de laquelle démarrer son exécution. Un script peut être considéré simplement comme un ensemble de commandes (instructions) qui sont exécutées à partir de la première instruction.

Un script peut être soit très simple, composé d'une seule commande, soit très complexe, contenant des dizaines, des centaines ou même des milliers d'instructions. Les instructions consécutives peuvent être séparées par des points-virgules (;). Cependant, cette exigence est facultative, donc tout le code ci-dessous est syntaxiquement correct :

Travailler avec des variables en Lua

Les variables sont utilisées pour stocker des valeurs lors de l'exécution du script.

Noms de variables en Lua

Les noms de variables (identifiants) en Lua peuvent être n'importe quelle séquence de lettres, de chiffres et le caractère de soulignement qui ne commence pas par un chiffre.

Remarque

Lua est sensible à la casse, donc abc, Abc, ABC sont des noms différents.

Le tableau ci-dessous montre les mots qui sont réservés par le langage Lua et ne peuvent pas être utilisés dans les noms de variables :

et casser faire d'autre sinon

fin faux pour la fonction si

en local nil pas ou

répéter return puis vrai jusqu'à ce que

De plus, tous les noms commençant par un trait de soulignement suivi de lettres majuscules (par exemple, _VERSION) sont également réservés.

Quelles variables y a-t-il dans Lua ?

Les variables dans Lua peuvent être globales ou locales. Si une variable n'est pas explicitement déclarée comme locale, elle est considérée comme globale.

Variables globales Lua

La variable globale apparaît lorsque la première valeur lui est affectée. Avant d'affecter la première valeur, l'appel à la variable globale renvoie nil.

MsgBox (tostring (g)) -> nil

MsgBox (tostring (g)) -> 1

La variable globale existe tant que l'environnement d'exécution du script existe et est disponible pour tout code Lua exécuté dans cet environnement.

Si nécessaire, vous pouvez supprimer explicitement une variable globale en lui affectant simplement une valeur nulle.

g = 1 - crée une variable globale g avec la valeur 1

g = nil - supprime la variable globale g

MsgBox (tostring (g)) -> nil

Toutes les variables globales sont des champs d'une table régulière appelée environnement global. Cette table est accessible via la variable globale _G. Puisque les champs de l'environnement global sont tous des variables globales (y compris _G lui-même), alors _G._G == _G.

Variables locales Lua

Toute variable locale doit être déclarée explicitement à l'aide du mot-clé local. Vous pouvez déclarer une variable locale n'importe où dans le script. La déclaration peut inclure l'attribution d'une valeur initiale à la variable. Si aucune valeur n'est affectée, la variable contient nil.

local a - déclare une variable locale a

local b = 1 - déclarer une variable locale b, lui attribuer la valeur 1

local c, d = 2, 3 - déclarer les variables locales c et d, leur attribuer les valeurs 2 et 3

La portée d'une variable locale commence après la déclaration et se poursuit jusqu'à la fin du bloc.

Noter

La portée d'une variable est un morceau de code de programme dans lequel vous pouvez accéder à la valeur stockée dans une variable donnée.

Un bloc s'entend comme :

le corps de la structure de contrôle (si-alors, sinon, pour, tandis que, répéter) ;

corps de fonction;

un morceau de code inclus dans les mots-clés do ... end.

Si une variable locale est définie en dehors de tout bloc, sa portée s'étend jusqu'à la fin du script.

local i = 1 - la variable i est locale dans le script

alors que je<= a do - цикл от 1 до 5

local a = i ^ 2 - la variable a est locale à l'intérieur de la boucle while

MsgBox (a) -> 1, 4, 9, 16, 25

MsgBox (a) ->

si je> 5 alors

local a - la variable a est locale à l'intérieur de then

MsgBox (a) -> 10

MsgBox (a) -> 5 (ici référence au global a)

local a = 20 - la variable a est locale à l'intérieur du do-end

MsgBox (a) -> 20

MsgBox (a) -> 5 (ici référence au global a)

Remarque

Dans la mesure du possible, il est recommandé d'utiliser des variables locales au lieu de variables globales. Cela évitera « d'encombrer » l'espace de noms global et offrira de meilleures performances (puisque l'accès aux variables locales dans Lua est un peu plus rapide qu'aux variables globales).

Types de données Lua

Quels types de données Lua prend-il en charge ?

Lua prend en charge les types de données suivants :

1. Néant (rien). Correspond au fait qu'une variable n'a pas de valeur. Ce type est représenté par une valeur unique, nil.

2. Booléen (logique). Ce type comprend les valeurs false et true.

Lors de l'exécution d'opérations logiques, nil est considéré comme faux. Toutes les autres valeurs, y compris le nombre 0 et la chaîne vide, sont considérées comme vraies.

3. Numéro (numérique). Sert à représenter des valeurs numériques.

Les constantes numériques peuvent contenir une partie fractionnaire facultative et un ordre décimal facultatif, spécifiés par les caractères "e" ou "E". Les constantes numériques entières peuvent être spécifiées en hexadécimal à l'aide du préfixe 0x.

Exemples de constantes numériques valides : 3, 3.0, 3.1415926, 314.16e-2, 0xff.

4. Chaîne (chaîne). Sert à représenter des chaînes.

Les valeurs de chaîne sont spécifiées sous la forme d'une séquence de caractères, entre guillemets simples ou doubles :

a = "ceci est une chaîne"

b = "c'est la deuxième ligne"

Les chaînes entre guillemets peuvent interpréter des séquences d'échappement de type C (séquences d'échappement) qui commencent par le caractère "\" (barre oblique inverse) :

\ b (espace),

\ n (saut de ligne),

\ r (retour chariot);

\ t (onglet horizontal),

\\ (barre oblique inverse);

\ "" (double citation);

\ "(simple citation).

Remarque

Un caractère dans une chaîne peut également être représenté par son propre code à l'aide d'une séquence d'échappement :

où ddd est une séquence de trois chiffres maximum.

En plus des guillemets, les doubles crochets peuvent également être utilisés pour définir une chaîne :

La définition d'une chaîne avec des crochets doubles permet d'ignorer toutes les séquences d'échappement, c'est-à-dire que la chaîne est créée entièrement comme décrit :

local a = [] en Lua] =]

Il y aura un terme : "définition de la chaîne [] en Lua"

5. Fonction. Les fonctions de Lua peuvent être écrites dans des variables, transmises en tant que paramètres à d'autres fonctions et renvoyées à la suite de l'exécution de fonctions.

6. Tableau. Une table est un ensemble de paires clé-valeur, appelées champs ou éléments de table. Les clés et les valeurs des champs de la table peuvent être de tout type sauf nil. Les tableaux n'ont pas de taille fixe : vous pouvez leur ajouter un nombre arbitraire d'éléments à tout moment.

Lire la suite dans l'article "Créer des tables en Lua"

7. Données utilisateur (données utilisateur). C'est un type de données spécial. Les valeurs de ce type ne peuvent pas être créées ou modifiées directement dans un script Lua.

Userdata est utilisé pour représenter de nouveaux types créés dans le programme d'appel de script ou dans des bibliothèques écrites en C. Par exemple, les bibliothèques d'extension Lua pour "CronosPRO" utilisent ce type pour représenter des objets tels que :

banques de données (classe Banque) ;

bases de données (classe de base);

records (classe Record), etc.

8. Enfiler (fil). Correspond au flux d'exécution. Ces flux ne sont en aucun cas liés au système d'exploitation et sont pris en charge exclusivement par Lua lui-même.

Comment définir le type d'une variable dans Lua ?

Lua ne définit pas explicitement le type d'une variable. Le type d'une variable est défini au moment où une valeur est affectée à la variable. N'importe quelle variable peut se voir affecter une valeur de n'importe quel type (quel que soit le type de valeur qu'elle contenait auparavant).

a = 123 - la variable a est de type numéro

a = "123" - maintenant la variable a est de type chaîne

a = vrai - maintenant la variable a est de type booléen

a = () - maintenant la variable a est de type table

Remarque

Les variables de type table, fonction, thread et userdata ne contiennent pas les données elles-mêmes, mais stockent des références aux objets correspondants. Lors de l'affectation, du passage à une fonction en tant qu'argument et du retour d'une fonction en conséquence, les objets ne sont pas copiés, seules les références à eux sont copiées.

a = () - crée un tableau. Une référence à la table est placée dans la variable a

b = a - la variable b renvoie au même tableau qu'a

a = 10 - l'élément du tableau d'indice 1 se voit attribuer la valeur 10

MsgBox (b) -> "10"

MsgBox (a) -> "20"

Le reste des données sont des valeurs immédiates.

MsgBox (a) -> "20"

MsgBox (b) -> "10"

Comment obtenir le type d'une variable en Lua ?

Le type de la valeur stockée dans une variable peut être connu à l'aide du type de fonction standard. Cette fonction retourne une chaîne contenant le nom du type ("nil", "number", "string", "boolean", "table", "function", "thread", "userdata").

t = type ("ceci est une chaîne") - t est égal à "chaîne"

t = type (123) - t est égal à "nombre"

t = type (type) - t est "fonction"

t = type (vrai) - t est "booléen"

t = type (nil) - t est "nil"

t = type (CroApp.GetBank ()) - t est égal à "userdata"

Comment convertir le type d'une variable en Lua ?

Lua convertit automatiquement les nombres en chaînes et vice versa selon les besoins. Par exemple, si une valeur de chaîne est un opérande dans une opération arithmétique, elle est convertie en nombre. De même, une valeur numérique qui apparaît là où une chaîne est attendue sera convertie en chaîne.

a = "10" + 2 - a égale 12

a = "10" + 2 - a est égal à "10 + 2"

a = "-5.3e-10" * "2" - a est égal à -1.06e-09

a = "chaîne" + 2 - Erreur ! Impossible de convertir "chaîne" en nombre

Toute valeur peut être explicitement convertie en chaîne à l'aide de la fonction tostring standard.

a = tostring (10) - a est égal à "10"

a = tostring (true) - a est égal à "true"

a = tostring (nil) - a est égal à "nil"

a = tostring ((= "ceci est le champ 1")) - a est égal à "table: 06DB1058"

Dans l'exemple précédent, vous pouvez voir que le contenu des tables n'est pas converti par la fonction tostring. Cette transformation peut être effectuée à l'aide de la fonction render.

a = rendu (10) - a est égal à "10"

a = rendu (vrai) - a est égal à "vrai"

a = rendu (nil) - a est égal à "nil"

a = rendu ((= "c'est le champ 1")) - a est égal à "(=" c'est le champ 1 ")"

Vous pouvez utiliser la fonction tonumber standard pour convertir explicitement une valeur en nombre. Si la valeur est une chaîne qui peut être convertie en nombre (ou est déjà un nombre), la fonction renvoie le résultat de la conversion, sinon elle renvoie nil.

a = tonumber ("10") - a est égal à "10"

a = tonombre ("10" .. ". 5") - a est égal à 10,5

a = tonumber (vrai) - a est "nil"

a = tonumber (nil) - a est "nil"

Organisation des commentaires en Lua

Un commentaire en Lua commence par deux signes moins (-) et se poursuit jusqu'à la fin de la ligne.

local a = 1 - commentaire sur une seule ligne

Si deux crochets ouvrants ([[)] suivent immédiatement les caractères « - », le commentaire est multiligne et continue jusqu'à deux crochets fermants (]]).

local a = 1 - [[multiligne

un commentaire ]]

Les doubles crochets dans les commentaires peuvent être imbriqués. Afin de ne pas les confondre, un signe égal (=) est inséré entre les parenthèses :

local a = [[Société Kronos]] - [= [

local a = [[Société Kronos]]

Le nombre de symboles "=" détermine l'imbrication :

local a = [= [définition d'une chaîne [] en langue Lua] =] - [== [

local a = [= [définition d'une chaîne [] en langue Lua] =]

Opérations Lua

Les types d'opérations suivants peuvent être utilisés dans les expressions écrites en Lua :

1. Opérations arithmétiques.

Lua prend en charge les opérations arithmétiques suivantes :

+ (addition) ;

- (soustraction);

* (multiplication) ;

/ (partie) ;

^ (exponentiation);

% (reste de la division).

Remarque

Les opérations arithmétiques s'appliquent à la fois aux nombres et aux chaînes, qui dans ce cas sont convertis en nombres.

2. Opérations de comparaison.

Lua permet les opérations de comparaison suivantes pour les valeurs :

== (égal);

~ = (pas égal);

< (меньше);

> (plus) ;

<= (меньше или равно);

> = (supérieur ou égal).

Remarque

Les opérations de comparaison renvoient toujours la valeur booléenne true ou false.

Les règles de conversion des nombres en chaînes (et vice versa) ne fonctionnent pas pour les comparaisons, c'est-à-dire que l'expression "0" == 0 est fausse.

3. Opérations logiques.

Les opérations logiques comprennent :

et (ET logique).

L'opérateur and renvoie son premier opérande s'il est faux ou nil. Sinon, l'opération renvoie le deuxième opérande (et cet opérande peut être de n'importe quel type).

a = (nil et 5) - a est nul

a == (faux et 5) - a est égal à faux

a == (4 et 5) - a est égal à 5

ou (OU logique).

L'opérateur ou renvoie le premier opérande s'il n'est pas faux ou nil, sinon il renvoie le deuxième opérande.

a == (4 ou 5) - a est égal à 4

a == (faux ou 5) - a est égal à 5

Remarque

Les opérations booléennes et et ou peuvent renvoyer des valeurs de tout type.

Les opérateurs logiques et et ou évaluent la valeur du deuxième opérande uniquement s'il doit être renvoyé. S'il n'est pas requis, le deuxième opérande n'est pas évalué. Par exemple:

a == (4 ou f ()) - la fonction f () ne sera pas appelée

non (NON logique).

L'opération not renvoie toujours vrai ou faux.

4. Opération de concaténation.

Pour concaténer (joindre) des chaînes, utilisez l'opération ... (deux points).

a = "Kronos" .. "-" .. "Inform" - la variable a recevra la valeur "Kronos-Inform"

Remarque

Si un ou les deux opérandes sont des nombres, ils sont convertis en chaînes.

a = 0..1 - la variable a recevra la valeur "01"

5. L'opération d'obtention de la longueur.

Lua définit un opérateur de longueur # qui peut être utilisé pour obtenir la longueur d'une chaîne.

a = "chaîne"

len = #a - len est 6

len = # "une autre ligne" - len vaut 10

Remarque

Vous pouvez également utiliser l'opération # pour connaître l'index (ou la taille) maximum d'un tableau. Plus de détails - dans l'article "Travailler avec des tableaux en Lua".

Opération prioritaire en Lua

En langage Lua, les opérations sont effectuées selon la priorité suivante (par ordre décroissant) :

2.pas # - (unaire)

6. < > <= >= ~= ==

Appel de scripts à partir de formulaires

Chaque formulaire (y compris les formulaires imbriqués) est associé à un script distinct, qui contient généralement des fonctions qui gèrent les événements du formulaire et de ses éléments.

Lorsque le formulaire est lancé, son script est chargé dans l'environnement global. Lorsqu'un événement d'un formulaire ou de son élément se produit, le système appelle la fonction gestionnaire associée à cet événement.

Il est à noter que le script de formulaire, bien qu'il ne contienne pas d'appel à la fonction module, est en fait un module. Cela signifie que les variables déclarées dans le script de formulaire sans le mot-clé local ne sont pas déplacées vers l'environnement global et ne sont disponibles que dans ce script. Si vous devez mettre une valeur à disposition de scripts d'autres formes, elle doit être explicitement définie dans la table globale _G :

local a = _G.var

Blocs d'instructions

Les principaux opérateurs Lua sont :

mission;

opérateur conditionnel ;

opérateurs pour l'organisation des boucles.

Un groupe d'instructions peut être combiné en un bloc (instruction composée) en utilisant la construction do… end.

do - le début du bloc

<оператор1>- corps de bloc

<оператор2>

<операторN>

fin - la fin du bloc

Le bloc ouvre une nouvelle portée dans laquelle vous pouvez définir des variables locales.

a = 5 - variable globale a

local a = 20 - à l'intérieur de la variable locale de fin a est défini

MsgBox (a) -> 20

MsgBox (a) -> 5 (ici l'appel est déjà vers le global a)

Opérateur d'affectation Lua

Une affectation modifie la valeur d'une variable ou d'un champ de table. Dans sa forme la plus simple, une tâche pourrait ressembler à ceci :

a = 1 - la variable a reçoit la valeur 1

a = b + c - la variable a se voit attribuer la somme des valeurs des variables b et c

a = f (x) - la variable a reçoit la valeur renvoyée par la fonction f (x)

Lua permet l'affectation dite multiple, lorsque plusieurs variables à gauche de l'opérateur d'affectation reçoivent les valeurs de plusieurs expressions écrites à droite de l'opérateur d'affectation :

a, b = 1,5 * c - a est égal à 1 ; b est égal à 5 ​​* c

S'il y a plus de variables que de valeurs, nil est affecté aux variables "extra".

a, b, c = 1, 2 - a vaut 1 ; b vaut 2; c est nul

S'il y a plus de valeurs que de variables, les valeurs "supplémentaires" sont ignorées.

a, b = 1, 2, 3 - a vaut 1 ; b vaut 2; valeur 3 non utilisée

L'affectation multiple peut être utilisée pour échanger des valeurs entre variables :

a = 10 ; b = 20 - a vaut 10, b vaut 20

a, b = b, a - maintenant a vaut 20, b vaut 10

Instruction conditionnelle (if) en Lua

L'instruction if teste la véracité de la condition donnée. Si la condition est vraie, la partie du code suivant le mot-clé then (la section then) est exécutée. Sinon, le code suivant le mot-clé else est exécuté (la section else).

si a> b alors

renvoie a - si a est supérieur à b, renvoie a

retourner b - sinon, retourner b

La section else est facultative.

si un< 0 then

a = 0 - si a est inférieur à 0, définissez a sur 0

Au lieu d'instructions if imbriquées, vous pouvez utiliser la construction elseif. Par exemple, le code suivant :

il sera plus facile à comprendre si vous le remplacez par ce qui suit :

renvoie "Ivan" - si a est égal à 1

sinon si a == 2 alors

renvoie "Pierre" - si a vaut 2

sinon si a == 3 alors

renvoie "Sergey" - si a vaut 3

retourner "Il n'y a pas de tel joueur" - si a n'est aucun de ce qui précède

Boucle avec précondition (en) en Lua

L'instruction while est conçue pour organiser des boucles avec une condition préalable et a la forme suivante :

tandis que faire

... - le corps du cycle

Avant chaque itération de la boucle, la condition est vérifiée :

si la condition est fausse, la boucle se termine et le contrôle est transféré à la première instruction suivant l'instruction while ;

si la condition est vraie, le corps de la boucle est exécuté, après quoi toutes les actions sont répétées.

tandis que i> 0 do - boucle de 10 à 1

t [i] = "champ" ..i

a = (3, 5, 8, -6, 5)

tandis que i> 0 do - recherche d'une valeur négative dans le tableau

si un [i]< 0 then break end - если найдено, прерываем цикл

i = i - 1 - sinon passer à l'élément suivant

si i> 0 alors

MsgBox ("Index de valeur négative :" ..i)

MsgBox ("Le tableau ne contient pas de valeurs négatives")

Noter

Boucle avec postcondition (répétition) en Lua

L'instruction repeat est conçue pour organiser des boucles avec une postcondition et se présente sous la forme suivante :

... - le corps du cycle

jusqu'à

Le corps de la boucle est exécuté tant que la condition ne deviendra pas vrai. La condition est vérifiée après l'exécution du corps de la boucle, par conséquent, dans tous les cas, le corps de la boucle sera exécuté au moins une fois.

Additionner les valeurs du tableau a jusqu'à ce que la somme dépasse 10

a = (3, 2, 5, 7, 9)

somme = somme + a [i]

jusqu'à somme> 10

MsgBox ("Stacked" ..i .. "items. Amount is" ..sum)

Vous pouvez utiliser l'instruction break pour quitter la boucle avant qu'elle ne se termine.

Noter

Plus de détails sur les fonctionnalités d'utilisation de l'opérateur de rupture - dans l'article "Les opérateurs s'interrompent et reviennent"

Lua pour les boucles

L'instruction for est conçue pour organiser des boucles et peut être écrite sous deux formes :

simple (numérique pour) ;

étendu (universel pour).

La forme simple de l'instruction for

La forme simple de l'instruction for ressemble à ceci :

pour var = exp1, exp2, exp3 faire

... - le corps du cycle

Le corps de la boucle est exécuté pour chaque valeur de la variable de boucle (compteur) var dans la plage de exp1 à exp2, avec un pas de exp3.

Noter

Le pas n'est peut-être pas défini. Dans ce cas, il est pris égal à 1.

pour i = 1, 10 do - boucle de 1 à 10 avec pas 1

MsgBox ("i est égal à" ..i)

pour i = 10, 1, -1 do - boucle de 10 à 1 avec un pas de -1

MsgBox ("i est égal à" ..i)

Remarque

Les expressions exp1, exp2 et exp3 ne sont évaluées qu'une seule fois, avant le début de la boucle. Ainsi, dans l'exemple ci-dessous, la fonction f (x) sera appelée pour calculer la limite supérieure de la boucle une seule fois :

pour i = 1, f (x) do - boucle de 1 à la valeur renvoyée par f ()

MsgBox ("i est égal à" ..i)

La variable de boucle est locale à l'instruction de boucle et n'est pas définie à sa fin.

for i = 1, 10 do - boucle de 1 à la valeur renvoyée par f ()

MsgBox ("i est égal à" ..i)

MsgBox ("Après avoir quitté la boucle, i est égal à" ..i) - Faux ! je suis nul

Remarque

La valeur d'une variable de boucle ne peut pas être modifiée à l'intérieur d'une boucle : les conséquences d'un tel changement sont imprévisibles.

Pour quitter la boucle avant qu'elle ne se termine, utilisez l'instruction break.

a = (3, 5, 8, -6, 5)

for i = 1, # a do - recherchez une valeur négative dans le tableau

si un [i]< 0 then - если найдено...

index = i - enregistre l'index de la valeur trouvée ...

rompre - et rompre la boucle

MsgBox ("Index de valeur négative :" ..index)

Noter

Plus de détails sur les fonctionnalités de l'utilisation de l'opérateur break - dans l'article "Les instructions break et return")

Je suis un programmeur sentimental. Parfois, je tombe amoureux des langages de programmation, et puis je peux en parler pendant des heures. Je partagerai une de ces heures avec vous.

Lua ? Qu'est-ce que c'est?

Lua est un langage simple intégrable (il peut être intégré à vos programmes écrits dans d'autres langages), léger et direct, avec un seul type de données, avec la même syntaxe. La langue parfaite à apprendre.

Pourquoi?

Lua pourrait vous être utile :

* si vous êtes un gamer (plugins pour World of Warcraft et bien d'autres jeux)
* si vous écrivez des jeux (très souvent dans les jeux, le moteur est écrit en C/C++, et AI - en Lua)
* si vous êtes un programmeur système (vous pouvez écrire des plugins pour nmap, wireshark, nginx et d'autres utilitaires en Lua)
* si vous êtes un développeur embarqué (Lua est très rapide, compact et nécessite très peu de ressources)

1. Apprenez à programmer. Au moins un peu. Peu importe la langue.
2. Installez Lua. Pour ce faire, téléchargez la version 5.2 ici (http://www.lua.org/download.html), ou recherchez-la dans les dépôts. La version 5.1 fonctionnera aussi, mais sachez qu'elle est très ancienne.

Exécutez tous les exemples de l'article dans le terminal avec une commande telle que "lua file.lua".

Premières impressions

Lua est un langage typé dynamiquement (les variables obtiennent des types "à la volée" en fonction des valeurs attribuées). Vous pouvez y écrire à la fois dans un style impératif et orienté objet ou fonctionnel (même si vous ne savez pas comment c'est, ce n'est pas grave, continuez à lire). Voici Hello world en Lua :

Ma première application lua : hello.lua print "hello world" ; imprimer ("au revoir monde")

Ce que l'on peut déjà dire sur la langue :

* les commentaires sur une seule ligne commencent par deux tirets "-"
* les parenthèses et les points-virgules peuvent être omis

Opérateurs de langue

L'ensemble des conditions et des boucles est assez typique :

Instructions conditionnelles (il ne peut y avoir aucune branche else) if a == 0 then print ("a is zero") else print ("a is not zero") end - forme abrégée if / elseif / end (au lieu de switch / case) if a == 0 then print ("zero") elseif a == 1 then print ("one") elseif a == 2 then print ("two") else print ("other") end - compteur boucle pour i = 1, 10 do print (i) end - boucle avec précondition b = 5 tandis que b> 0 do b = b - 1 end - boucle avec postcondition répéter b = b + 1 jusqu'à b> = 5

PENSEZ : que pourrait signifier la boucle "for i = 1, 10, 2 do ... end" ?

Dans les expressions, vous pouvez utiliser les opérateurs suivants sur les variables :

* affectation : x = 0
* arithmétique : +, -, *, /,% (reste de la division), ^ (exponentiation)
* booléen : et, ou, non
* comparaison :>,<, ==, <=, >=, ~ = (pas-égal, oui-oui, au lieu de l'habituel "! =")
* concaténation de chaînes (opérateur ".."), par exemple : s1 = "hello"; s2 = "monde" ; s3 = s1..s2
* longueur / taille (opérateur #): s = "bonjour"; a = #s ('a' sera 5).
* obtenir un élément par index, par exemple : s

Pendant longtemps, il n'y avait pas d'opérations au niveau du bit dans le langage, mais dans la version 5.2 est apparue la bibliothèque bit32, qui les implémente (en tant que fonctions, pas en tant qu'opérateurs).

Types de données

Je vous ai menti quand j'ai dit que la langue a un type de données. Il en a beaucoup (comme tout langage sérieux) :

* néant (rien du tout)
* nombres booléens (vrai/faux)
* nombres (nombres) - pas de division par des nombres entiers / réels. Juste des chiffres.
* cordes - d'ailleurs, elles ressemblent beaucoup aux cordes de pascal
* fonctions - oui, une variable peut être de type "fonction"
* fil
* données arbitraires (userdata)
* table

Si tout est clair avec les premiers types, alors qu'est-ce que userdata ? N'oubliez pas que Lua est un langage intégrable et travaille généralement en étroite collaboration avec des composants de programmes écrits dans d'autres langages. Ainsi, ces composants "étrangers" peuvent créer des données en fonction de leurs besoins et stocker ces données avec des objets lua. Ainsi, les données utilisateur sont la partie sous-marine de l'iceberg, qui du point de vue du langage lua n'est pas nécessaire, mais nous ne pouvons tout simplement pas l'ignorer.

Et maintenant, la chose la plus importante dans la langue, ce sont les tableaux.

les tables

Je vous ai encore menti quand j'ai dit que le langage a 8 types de données. Vous pouvez supposer qu'il en est un : tout est des tableaux (ce qui, d'ailleurs, n'est pas vrai non plus). Une table est une structure de données très élégante, elle combine les propriétés d'un tableau, d'une table de hachage ("clé" - "valeur"), d'une structure, d'un objet.

Donc, voici un exemple de tableau sous forme de tableau : a = (1, 2, 3) - un tableau de 3 éléments print (a) - affichera "2", car les indices sont comptés à partir d'un - Un tableau est un tableau clairsemé (qui n'a pas tous les éléments) a = () - table vide a = 1 a = 5

PENSEZ : Qu'est-ce qu'un tableau clairsemé ?

Dans l'exemple ci-dessus, la table se comporte comme un tableau, mais en fait - nous avons des clés (index) et des valeurs (éléments de tableau). Et en même temps, les clés peuvent être de n'importe quel type, pas seulement des chiffres :

A = () a ["hello"] = true a ["world"] = false a = 1 - ou comme ceci : a = (hello = 123, world = 456) print (a ["hello")) print ( a.hello) est identique à un ["hello"], bien qu'il ressemble à une structure avec des champs

Soit dit en passant, puisque la table a des clés et des valeurs, vous pouvez parcourir toutes les clés et leurs valeurs correspondantes dans une boucle :

T = (a = 3, b = 4) pour la clé, valeur par paires (t) faire imprimer (clé, valeur) - imprime "a 3", puis "b 4" fin

Mais qu'en est-il des objets ? Nous en apprendrons un peu plus tard, d'abord - sur les fonctions.

Les fonctions

Voici un exemple de fonction courante.

Fonction add (a, b) return a + b end print (add (5, 3)) - affichera "8"

Les fonctions de langage vous permettent de prendre plusieurs arguments et de retourner plusieurs arguments. Par exemple, les arguments dont les valeurs ne sont pas spécifiées explicitement sont supposés être nuls.

PENSEZ : Pourquoi voudriez-vous retourner plusieurs arguments ?

Fonction swap (a, b) return b, a end x, y = swap (x, y) - d'ailleurs, cela peut se faire sans fonction : x, y = y, x - et si la fonction retourne plusieurs arguments , - et vous n'en avez pas besoin - ignorez-les avec - la variable de soulignement spéciale "_" a, _, _, d = some_function ()

Les fonctions peuvent prendre un nombre variable d'arguments :

Dans le prototype, le nombre variable d'arguments est écrit sous forme de points de suspension, fonction somme (...) s = 0 pour _, n par paires (arg) do - dans la fonction, ils sont appelés la table "arg" s = s + n end return a end sum (1, 2, 3) - renverra 6 sum (1, 2, 3, 4) - renverra 10

Les fonctions étant un type de données à part entière, vous pouvez créer des variables de fonction ou transmettre des fonctions en tant qu'arguments à d'autres fonctions.

A = fonction (x) retour x * 2 fin - fonction qui multiplie par 2 b = fonction (x) retour x + 1 fin - fonction qui augmente de 1 fonction appliquer (table, f) résultat = () pour k, v dans paires (table) font résultat [k] = f (v) - remplacez l'élément par une fonction de cet élément end end - PENSEZ : quels appels retourneront t = (1, 3, 5) appliquez (t, a) appliquez ( t, b)

Objets = fonctions + tableaux

Puisque nous pouvons stocker des fonctions dans des variables, nous pouvons également le faire dans des champs de table. Et cela s'avère déjà, pour ainsi dire, des méthodes. Pour ceux qui ne sont pas familiers avec la POO, je dirai que son principal avantage (au moins en Lua) est que les fonctions et les données avec lesquelles elles travaillent sont côte à côte - au sein du même objet. Pour ceux qui connaissent la POO, je dirai qu'il n'y a pas de classes ici et que l'héritage est prototypique.

Passons aux exemples. Nous avons un objet, disons, une ampoule. Elle sait brûler et ne pas brûler. Eh bien, il y a deux actions que vous pouvez faire avec - l'activer et le désactiver :

Lampe = (on = false) fonction turn_on (l) l.on = true end fonction turn_off (l) l.on = false end ne sont que des fonctions pour travailler avec la structure turn_on (lampe) turn_off (lampe)

Et si nous transformons l'ampoule en objet, et transformons les fonctions turn_off et turn_on en champs de l'objet, nous obtenons :

Lamp = (on = false turn_on = fonction (l) l.on = true end turn_off = fonction (l) l.on = false end) lamp.turn_on (lampe) lamp.turn_off (lampe)

Nous sommes obligés de passer l'objet ampoule lui-même comme premier argument, car sinon notre fonction ne saura pas avec quelle ampoule elle doit fonctionner pour changer l'état marche / arrêt. Mais pour ne pas être verbeux, Lua a un raccourci qui est généralement utilisé - lampe : turn_on (). Au total, nous connaissons déjà plusieurs de ces simplifications syntaxiques :

Lamp: turn_on () - la notation la plus courante lamp.turn_on (lampe) - du point de vue de la syntaxe, c'est aussi la lampe correcte ["turn_on"] (lampe) - et ceci

En continuant à parler d'abréviations, les fonctions peuvent être décrites non seulement explicitement, en tant que champs d'une structure, mais aussi sous une forme plus pratique :

Lamp = (on = false) - à travers une période, alors l'argument doit être spécifié fonction lamp.turn_on (l) l.on = true end - à travers les deux-points, alors l'argument est implicitement défini comme la variable "self" - "self " - et il y a la lampe pour laquelle la fonction lampe méthode a été appelée : turn_off() self.on = false end

Intéressant?

Fonctions spéciales

Certains noms de fonctions (méthodes) de table sont réservés et ont une signification particulière :

* __add (a, b), __sub (a, b), __div (a, b), __mul (a, b), __mod (a, b), __pow (a, b) - appelé lorsque des opérations arithmétiques sont effectuées sur table
* __unm (a) - opération moins unaire (quand ils écrivent quelque chose comme "x = -x")
* __lt (a, b), __le (a, b), __eq (a, b) - calculer le résultat de la comparaison (<, <=, ==)
* __len (a) - appelé lorsque "#a" est terminé
* __concat (a, b) - appelé quand "a..b"
* __call (a, ...) - appelé quand "a ()". Les arguments variables sont des arguments lorsqu'ils sont appelés
* __index (a, i) - appel à un [i], à condition qu'un tel élément n'existe pas
* __newindex (a, i, v) - création de "a [i] = v"
* __gc (a) - lorsqu'un objet est supprimé par le ramasse-miettes

En remplaçant ces méthodes, vous pouvez surcharger les opérateurs et utiliser la syntaxe du langage à vos propres fins. L'essentiel est de ne pas en faire trop.

Héritage

Pour ceux qui ne connaissent pas la POO, l'héritage permet d'étendre les fonctionnalités d'une classe existante. Par exemple, une simple ampoule peut s'allumer et s'éteindre, et une ampoule ultra-légère modifiera également sa luminosité. Pourquoi devons-nous réécrire les méthodes turn_on/turn_off alors que nous pouvons les réutiliser ?

Lua a le concept d'une table méta pour cela, c'est-à-dire tableaux d'ancêtres. Chaque table a une table parent et la table enfant peut faire tout ce que le parent peut faire.

Disons que nous avons déjà créé l'objet lamp table. Ensuite, la super ampoule ressemblera à ceci:

Superlamp = (luminosité = 100) - spécifiez la table parent setmetatable (superlamp, lamp) - et ses méthodes sont maintenant disponibles superlamp : turn_on () superlamp : turn_off ()

Extension des fonctionnalités

Il existe de nombreux types de tables parents (enfin, les chaînes et les tables, bien sûr, les nombres et les booléens, et nil pas). Disons que nous voulons ajouter toutes les lignes en utilisant l'opérateur "+", pas "..". Pour ce faire, remplacez la fonction "+" (__add) pour la table parente de toutes les lignes :

S = getmetatable ("") - a obtenu la table parent de la ligne s .__ add = function (s1, s2) return s1..s2 end - a changé la méthode - check a = "hello" b = "world" print ( a + b) - écrira "helloworld"

En fait, nous pouvons toujours remplacer la fonction d'impression par "print = myfunction", et de nombreuses autres choses de piratage peuvent être faites.

Portées

Les variables sont globales et locales. Une fois créées, toutes les variables de Lua sont globales.

PENSEZ : pourquoi ?

Pour spécifier la portée locale, écrivez le mot-clé local :

Local x local var1, var2 = 5, 3

N'oubliez pas ce mot.

Traitement des erreurs

Souvent, si des erreurs se produisent, vous devez arrêter d'exécuter une fonction particulière. Vous pouvez, bien sûr, faire de nombreuses vérifications et appeler "retour" si quelque chose ne va pas. Mais cela augmentera la quantité de code. Lua utilise quelque chose comme des exceptions.

Les erreurs sont renvoyées à l'aide de la fonction d'erreur (x). Tout peut être passé en argument (ce qui est pertinent pour l'erreur - une description de chaîne, un code numérique, l'objet avec lequel l'erreur s'est produite, etc.)

Habituellement, après cette fonction, tout le programme se bloque. Et ce n'est pas toujours nécessaire. Si vous appelez une fonction qui peut générer une erreur (ou que ses fonctions enfants peuvent générer une erreur), appelez-la en toute sécurité à l'aide de pcall() :

Fonction f (x, y) ... if ... then error (" n'a pas réussi à faire quelque chose ") end ... end status, err = pcall (f, x, y) - f : fonction, xy : ses arguments si ce n'est pas le statut alors - gérer l'erreur err. Dans notre cas, err contient le texte d'erreur end

Bibliothèques standards

Il existe de nombreuses bibliothèques non standard, elles peuvent être trouvées sur LuaForge, LuaRocks et d'autres référentiels.

Entre Lua et Non-Lua

Et si la fonctionnalité des bibliothèques standards ne nous suffisait pas ? Et si nous avons notre programme C et que nous voulons appeler ses fonctions depuis Lua ? Il existe un mécanisme très simple pour cela.

Disons que nous voulons créer notre propre fonction qui renvoie un nombre aléatoire (Lua a math.random()), mais nous voulons apprendre). Nous devrons écrire le code suivant en C :

#comprendre #comprendre #comprendre / * en fait que faire lors de l'appel de `rand (from, to)` * / static int librand_rand (lua_State * L) (int from, to; int x; from = lua_tonumber (L, 1); / * premier paramètre du fonction * / to = lua_tonumber (L, 2); / * deuxième paramètre de la fonction * / x = rand ()% (to - from + 1) + from; lua_pushnumber (L, x); / * valeur de retour * / return 1; / * ne retourne qu'un argument * /) / * dans Lua "rand" correspond à notre fonction librand_rand() * / static const luaL_reg R = (("rand", librand_rand), (NULL, NULL) / * end de la liste des fonctions exportées * / ); / * appelé lorsque la bibliothèque est chargée * / LUALIB_API int luaopen_librand (lua_State * L) (luaL_openlib (L, "librand", R, 0); srand (time (NULL)); return 1; / * success * /)

Celles. Lua nous fournit des fonctions pour travailler avec des types de données, pour recevoir des arguments de fonction et renvoyer des résultats. Les fonctions sont rares. Nous construisons maintenant notre bibliothèque en tant que bibliothèque dynamique et nous pouvons utiliser la fonction rand() :

Random = require ("librand") - charge la bibliothèque print (random.rand (1, 100)) print (random.rand (0, 1))

Et si nous voulons appeler le code Lua depuis nos programmes ? Ensuite, nos programmes devraient créer une machine virtuelle Lua, dans laquelle les scripts Lua seront exécutés. C'est beaucoup plus simple :

#include "lua.h" #include "lauxlib.h" int main () (lua_State * L = lua_open (); // crée une machine virtuelle Lua luaL_openlibs (L); // charge la bibliothèque standard luaL_dofile (L, " rand. lua "); // exécute le script lua_close (L); // ferme Lua return 0;)

Tout.

Vous pouvez maintenant écrire en Lua. Si vous découvrez des points intéressants sur Lua qui pourraient être reflétés dans l'article, écrivez !

© 2021 hecc.ru - Actualités informatiques