Fondamentaux et Programmation Orientée Objet en Lua

Indexation et structure des tables

Contrairement à de nombreux langages C-like, Lua utilise une indexation à base 1 pour ses séquences. Les tables constituent la seule structure de données native, fonctionnant simultanément comme des tableaux et des dictionnaires associatifs.

local repertoire = {
    [0] = 10,             -- Clé numérique explicite
    identifiant = 42,     -- Clé chaîne implicite
    ["code_postal"] = "75001"
}

print(repertoire[0])
print(repertoire["code_postal"])
print(repertoire["identifiant"])
print(repertoire.identifiant) -- Syntaxe abrégée

Opérations sur les chaînes

La manipulation textuelle repose sur l'opérateur de concaténation .. et un ensemble de fonctions natives. La conversion de type s'effectue via tostring() et tonumber().

local prefixe = "Utilisateur_"
local id_session = 804

-- Concaténation et conversion implicite
local etiquette = prefixe .. tostring(id_session)

-- Longueur et transformation de casse
local texte_original = "Données_CRITIQUES"
local longueur = string.len(texte_original)
local texte_normalise = string.lower(texte_original)

-- Extraction et formatage
local sous_chaine = string.sub(texte_original, 1, 7)
local message_formate = string.format("Taille: %d, Extraits: %s", longueur, sous_chaine)

Pour la recherche et le remplacement, string.find localise un motif tandis que string.gsub effectue des substitutions. Le quatrième paramètre de gsub limite le nombre de remplacements.

local phrase = "Système d'exploitation Linux"
local resultat = string.gsub(phrase, "Linux", "Windows", 1) -- Un seul remplacement

Parcours et agrégation de données

Une table Lua mélange des parties séquentielles et associatives. L'opérateur # retourne uniquement la longueur de la partie tableau contiguë.

local mesures = {10, 25, 30, 45, 50}
mesures[8] = 99 -- Création d'une entrée discontinue

-- Parcours séquentiel sécurisé
for idx = 1, #mesures do
    print(mesures[idx])
end

-- Itération avec ipairs (arrête aux premiers trous)
for indice, valeur in ipairs(mesures) do
    print(indice, valeur)
end

-- Itération exhaustive avec pairs (inclut les clés non séquentielles)
for cle, contenu in pairs(mesures) do
    print(cle, contenu)
end

La fonction table.unpack (anciennement unpack) permet de désagréger un tableau en arguments multiples, utile pour le repositionnement ou l'appel de fonctions variadiques.

local serie_temporelle = {20, 45, 80, 120, 160}
local _, _, valeur_mediane, pic = table.unpack(serie_temporelle)
print(valeur_mediane, pic) -- Retourne 80, 120

Chargement de scripts et encapsulation

La fonction require charge et exécute un script une seule fois, le mettant en cache. Elle retourne la valeur finale retournée par le fichier. Pour exposer des fonctionnalités, il est recommandé de retourner une table contenant les méthodes publiques.

-- Dans utils.lua
local outils = {}
function outils.calculer_somme(a, b) return a + b end
return outils

-- Dans le fichier principal
local module_utilitaire = require("utils")
print(module_utilitaire.calculer_somme(10, 5))

Sémantique du point et des deux-points

La distinction entre . et : réside dans la gestion implicite du contexte self. Le deux-points injecte automatiquement la table appelante comme premier argument, tandis que le point exige une transmission explicite.

local controleur = {}

-- Définition avec point : requiert 'contexte' explicitement
function controleur.initialiser(contexte, parametre)
    print("Initialisation via point", contexte, parametre)
end

-- Définition avec deux-points : 'self' est injecté automatiquement
function controleur:lancer(mode)
    print("Exécution via deux-points", self, mode)
end

controleur.initialiser(controleur, "valeur") -- Appel standard
controleur:lancer("actif") -- self pointe sur controleur
controleur.lancer(controleur, "actif") -- Équivalent explicite

Une incohérence entre la définition et l'appel (par exemple définir avec : mais appeler avec .) entraîne une perte de référence self, souvent évaluée à nil.

Métatables et résolution dynamique

Les métatables interceptent les opérations absentes dans la table cible. Le champ \_\_index est crucial : si une clé n'existe pas dans la table primaire, l'interpréteur consulte la métatable.

local entite_vide = {}
local configuration_par_defaut = {
    __index = {
        niveau_vie = 100,
        vitesse = 5.5,
        etat = "inactif"
    }
}

setmetatable(entite_vide, configuration_par_defaut)
print(entite_vide.niveau_vie) -- Retourne 100 via résolution __index

Modélisation orientée objet

Vue l'absence de classes natives, Lua simule la POO en chaînant les métatables. Un constructeur attache la table classe comme \_\_index de l'instance.

local Entite = {}
Entite.__index = Entite

function Entite:nouveau(nom)
    local instance = setmetatable({}, self)
    instance.titulaire = nom
    return instance
end

function Entite:afficher_identite()
    print("Identité :", self.titulaire)
end

local personnage = Entite:nouveau("Aventurier")
personnage:afficher_identite()

Héritage et sucrharge de méthodes

L'héritage se construit en faisant pointer \_\_index du dérivé vers la classe mère. Lors d'un appel de méthode, Lua remonte la chaîne des métatables jusqu'à trouver une implémentation.

local Personnage = Entite -- Hérite de la structure précédente

function Personnage:attaquer()
    print(self.titulaire .. " frappe avec une épée")
end

-- Définition du dérivé
local Archer = setmetatable({}, {__index = Personnage})
Archer.__index = Archer

function Archer:nouveau(nom)
    local instance = setmetatable({}, self)
    instance.titulaire = nom
    return instance
end

function Archer:attaquer()
    print(self.titulaire .. " tire une flèche")
end

local archer = Archer:nouveau("Legolas")
archer:attaquer() -- Résolu dans Archer
archer:afficher_identite() -- Résolu dans Entite via le chaînage

Lorsqu'une méthode est redéfinie dans le dérivé, elle masque la version parente. Pour invoquer explicitement la logique de la classe mère tout en préservant le contexte actuel, il faut utiliser la notation pointée et transmettre manuellement l'instance :

function Archer:attaquer()
    print(self.titulaire .. " tire une flèche")
    Personnage.attaquer(self) -- Appel explicite vers la classe mère
end

Cette approche garantit que le parent opère sur les données de l'instance fille, respectant les principes de polymorphisme et de délégation propre au modèle objet Lua.

Étiquettes: Lua ProgrammationOrientéeObjet Metatables scripting

Publié le 29 juillet à 19h34