Fonctions globales
print, read, sleep, conversions, boucles, erreurs et imports : les fonctions que tout programme appelle sans nom de bibliothèque.
Ces fonctions sont toujours là : pas de nom de bibliothèque devant, rien à charger, et tous les ordinateurs les ont, du Calculateur à tubes à l'Ordinateur moderne. Elles affichent du texte et lisent le clavier (print, read), mettent le programme en pause (sleep), convertissent et examinent des valeurs (tonumber, type), parcourent des tables (pairs, ipairs), gèrent les erreurs (pcall) et découpent un programme en plusieurs fichiers (import).
local lignes = {"12", "15", "oups", "9"} -- mesures lues dans un journal
local total = 0
for i, texte in ipairs(lignes) do
local n = tonumber(texte)
if n then
total = total + n
else
print("la ligne " .. i .. " n'est pas un nombre : " .. texte)
end
end
print("total", total)la ligne 3 n'est pas un nombre : oups total 36
Une variable créée sans local est elle aussi une globale, rangée au même endroit que ces fonctions. Appelez une variable type ou print, et la fonction disparaît pour tout le programme : type = peripheral.type("left") casse tous les appels suivants à type(...). Choisissez un autre nom (genre, typeBloc) ou utilisez local.
print(...) | Affiche des valeurs à l'écran, puis va à la ligne. |
write(...) | Affiche du texte sans aller à la ligne. |
read([mask]) | Attend une ligne tapée au clavier et la renvoie. |
sleep(seconds) | Met le programme en pause pendant un certain nombre de secondes. |
tostring(value) | Convertit une valeur en texte. |
tonumber(value [, base]) | Convertit un texte en nombre, ou renvoie nil. |
type(value) | Donne le type d'une valeur : "nil", "number", "string", "boolean", "table" ou "function". |
pairs(t) | Parcourt toutes les clés et valeurs d'une table : for k, v in pairs(t) do. |
ipairs(t) | Parcourt t[1], t[2]... jusqu'au premier nil. |
error(message) | Arrête le programme avec une erreur. |
pcall(f, ...) | Appelle une fonction et attrape ses erreurs : vous obtenez {ok=true, value=...} ou {ok=false, error="..."}. |
assert(value [, message]) | Arrête le programme avec une erreur si la valeur est false ou nil ; sinon la renvoie. |
import(path) | Exécute un autre fichier une seule fois et renvoie ce qu'il renvoie : import "lib/outils". |
require(path) | Comme import. |
arg | Les mots tapés après le nom du programme à l'invite, sous forme de liste de textes. |
Écran et clavier
print et write écrivent là où se trouve le curseur, avec les couleurs choisies par term.set_fg et term.set_bg. Quand le texte atteint le bord droit, il continue au début de la ligne suivante, coupé n'importe où (pas entre deux mots). Quand le curseur dépasse la dernière ligne, tout l'écran remonte d'une ligne. Pour écrire à un endroit précis sans retour à la ligne ni défilement, utilisez term.set_cursor et term.write (voir term).
Affiche des valeurs à l'écran, puis va à la ligne.
valuesany- autant de valeurs que vous voulez, de n'importe quel type
Chaque valeur est convertie comme le fait tostring : les nombres sans .0 inutile, true, false, nil. Plusieurs valeurs sont séparées par une tabulation, qui avance jusqu'à la prochaine colonne multiple de 4 : pratique pour jeter un œil à quelques nombres, mais utilisez string.format pour de vraies colonnes. print() sans rien affiche une ligne vide.
print("Lingots de fer :", 128)
print("En marche", true, nil)
print(10, 200, 3000)
print()
print("fini")Lingots de fer : 128 En marche true nil 10 200 3000 fini
Un "\n" dans le texte commence une nouvelle ligne, et une ligne plus longue que l'écran (51 colonnes sur un Micro-ordinateur, 40 sur un Calculateur à tubes ou un Microcontrôleur, 64 sur un Ordinateur moderne) continue sur la suivante :
print("Ligne 1\nLigne 2")
print(string.rep("=", 60))Ligne 1 Ligne 2 =================================================== =========
Les caractères impossibles à afficher (les codes sous 32, sauf la tabulation et le retour à la ligne) apparaissent comme ?.
Un appel coûte une instruction, plus une par tranche de 16 caractères affichés. Une ligne pleine de 51 caractères coûte 4 : rien pour un Ordinateur moderne, mais un Calculateur à tubes n'exécute que 20 instructions par tick, et un écran rempli de texte lui prend quelques ticks. Voir Vitesse, mémoire et limites.
Voir aussi write() term.write() tostring()
Affiche du texte sans aller à la ligne.
valuesany- autant de valeurs que vous voulez, de n'importe quel type
Les valeurs sont converties en texte et écrites l'une après l'autre, sans rien entre elles (contrairement à print, qui met une tabulation). Le curseur reste au bout : le write, print ou read suivant continue sur la même ligne. Tabulations, retours à la ligne, coupure en bord d'écran et défilement marchent comme avec print.
write("Fonte")
for i = 1, 3 do
write(".")
end
write(" terminé", "\n")
write("Lot ", 4, " sur ", 10)
print()Fonte... terminé Lot 4 sur 10
Son usage le plus courant : la question avant un read, la réponse se tape juste derrière.
write("Combien de caisses ? ")
local reponse = read()Voir aussi print() read() term.write()
Attend une ligne tapée au clavier et la renvoie.
maskstring facultatif- un caractère affiché à la place de chaque caractère tapé (seul le premier caractère compte)
- string
- la ligne tapée, sans la touche Entrée
Le programme s'arrête dans read jusqu'à ce qu'un joueur, dans l'écran de l'ordinateur, tape une ligne et appuie sur Entrée. Le texte s'affiche là où se trouve le curseur : une question écrite avec write juste avant reste sur la même ligne. Après Entrée, le curseur passe au début de la ligne suivante.
write("Nom de la nouvelle gare : ")
local nom = read()
print("Gare '" .. nom .. "' enregistrée.")Ce que vous récupérez :
- Toujours un texte, même si le joueur a tapé des chiffres : convertissez-le avec
tonumberavant de calculer. ""(un texte vide) si le joueur appuie sur Entrée sans rien taper.- 1024 caractères au plus. Une ligne plus longue que la place restante à l'écran défile de côté pendant la frappe.
Le joueur peut corriger la ligne avant d'appuyer sur Entrée : Retour arrière, Suppr, les flèches gauche et droite, Début et Fin. Ctrl+V colle le presse-papiers, jusqu'à son premier retour à la ligne. Les flèches haut et bas et Tab ne font rien ici : l'historique et la complétion n'existent qu'à l'invite du shell.
Cacher un mot de passe. Avec un masque, chaque caractère tapé s'affiche sous la forme de ce caractère. Seul son premier caractère sert : read("*") et read("*#") affichent tous deux *. Un masque vide "" ne cache rien, et un masque qui n'est pas un texte arrête le programme avec bad argument #1 to 'read' (string expected, got number).
write("Mot de passe : ")
local code = read("*")
if code == "laiton42" then
print("Bienvenue")
rs.set("left", true) -- ouvre la porte à pistons de gauche
sleep(3)
rs.set("left", false)
else
print("Mot de passe incorrect")
endLa recette Porte à code construit une porte complète autour de ce principe.
Un menu. Affichez les choix, lisez un numéro, agissez, recommencez. L'image ci-dessous montre l'écran pendant que read attend :
local choix = {"Démarrer la ligne de presses", "Arrêter la ligne de presses", "Quitter"}
while true do
term.clear()
term.set_cursor(1, 1)
term.set_fg(term.colors.yellow)
print("== Ligne de presses mécaniques ==")
term.set_fg(term.colors.white)
for i, texte in ipairs(choix) do
print(i .. ". " .. texte)
end
print()
write("Votre choix : ")
local n = tonumber(read())
if n == 1 then
rs.set("back", false) -- un embrayage non alimenté laisse tourner l'arbre
print("Ligne démarrée")
elseif n == 2 then
rs.set("back", true) -- un embrayage alimenté l'arrête
print("Ligne arrêtée")
elseif n == 3 then
break
else
print("Tapez 1, 2 ou 3")
end
sleep(1)
end
Pendant que read attend, votre programme ne fait rien d'autre. Seuls les évènements du clavier (char, key et paste) vont dans la ligne. Tous les autres (un changement de redstone, une minuterie, un message réseau, un clic) sont gardés dans la file, dans l'ordre, et le prochain os.pull_event les reçoit. Les touches frappées avant l'appel à read, pendant un sleep par exemple, sont gardées elles aussi : read les utilise dès qu'il démarre (un os.pull_event avec un filtre, au contraire, jette les évènements qu'il saute, touches comprises). Pour réagir à la redstone pendant qu'un joueur tape, lisez vous-même le clavier avec os.pull_event (voir Évènements et Clavier).
Ctrl+T (ou le bouton Stopper) arrête le programme même pendant qu'il attend dans read.
Voir aussi write() tonumber() os.pull_event()
Attendre
Met le programme en pause pendant un certain nombre de secondes.
secondsnumber- la durée de l'attente, en secondes (décimales permises)
Minecraft avance de 20 ticks par seconde, et l'ordinateur compte ses attentes en ticks : sleep attend secondes × 20 ticks, arrondi au-dessus à un tick entier, et toujours au moins un tick.
| appel | ticks | durée réelle |
|---|---|---|
sleep(1) | 20 | 1 s |
sleep(0.5) | 10 | 0,5 s |
sleep(0.07) | 2 | 0,1 s |
sleep(0) ou un nombre négatif | 1 | 0,05 s |
local function ticks_de(secondes)
local debut = os.time()
sleep(secondes)
return os.time() - debut
end
print(ticks_de(1), ticks_de(0.5), ticks_de(0.07), ticks_de(0))20 10 2 1
Un compte à rebours avant de fermer un portail :
for s = 5, 1, -1 do
print("Fermeture du portail dans " .. s .. " s")
sleep(1)
end
rs.set("top", true) -- le piston mécanique ferme le portail
print("Portail fermé")Fermeture du portail dans 5 s Fermeture du portail dans 4 s Fermeture du portail dans 3 s Fermeture du portail dans 2 s Fermeture du portail dans 1 s Portail fermé
Pendant qu'il dort, l'ordinateur n'exécute aucune instruction, et le reste des instructions du tick en cours n'est pas gardé pour plus tard. Une boucle avec un sleep dedans est donc la bonne façon de répéter un travail à intervalles réguliers sans gaspiller le processeur, et une boucle avec sleep(0) tourne au plus 20 fois par seconde. Le temps est celui du monde : la vitesse de rotation de l'ordinateur ne le change pas, un serveur qui rame l'allonge. Sans rotation, en revanche, l'ordinateur est figé, et l'attente avec lui : elle reprend quand l'arbre tourne de nouveau.
Les évènements qui arrivent pendant l'attente ne sont pas perdus. Ils patientent dans la file (256 au plus, le plus ancien part quand elle est pleine) jusqu'au prochain os.pull_event ou read :
os.queue_event("remplir")
sleep(1)
local e = os.pull_event()
print(e.name .. " était encore là")remplir était encore là
Pour attendre « un signal de redstone ou 5 secondes, le premier des deux », sleep ne suffit pas : lancez une minuterie avec os.start_timer et attendez avec os.pull_event (voir Temps et minuteries).
Une durée qui n'est pas un nombre arrête le programme : bad argument #1 to 'sleep' (number expected, got string).
Voir aussi os.start_timer() os.pull_event() os.clock()
Valeurs et conversions
Convertit une valeur en texte.
valueany- n'importe quelle valeur
- string
- la valeur sous forme de texte
.. transforme déjà les nombres en texte tout seul, mais pas true, false ni nil : "alimenté : " .. true arrête le programme avec attempt to concatenate a boolean value. C'est là qu'il faut tostring.
| valeur | texte |
|---|---|
42, -7, 2.5 | "42", "-7", "2.5" |
true, false, nil | "true", "false", "nil" |
| une table | "table: 0x1b6d3586" (le nombre change d'une table à l'autre) |
| une de vos fonctions, un itérateur | "function: 0x..." |
une fonction intégrée comme print | "builtin: 0x..." |
Les nombres s'écrivent de la même façon partout (avec print, .., tostring, table.concat) : les entiers sous 1015 sans virgule, les autres avec au plus 14 chiffres significatifs, en notation exponentielle quand ils sont très grands ou très petits. Le séparateur décimal est toujours un point. La division par zéro donne inf ou -inf, et 0 / 0 donne nan (« pas un nombre »).
print(tostring(64) .. " objets")
print(0.1 + 0.2, 1 / 3)
print(123456789 * 1000000000, 0.00001)
print(1 / 0, -1 / 0, 0 / 0)
local alimente = true
print("alimenté : " .. tostring(alimente))64 objets 0.3 0.33333333333333 1.23456789e+17 1e-05 inf -inf nan alimenté : true
Pour un nombre fixe de décimales (3.10) ou des colonnes alignées, utilisez plutôt string.format.
Voir aussi tonumber() string.format()
Convertit un texte en nombre, ou renvoie nil.
valuestring|number- le texte à convertir (un nombre revient tel quel)
basenumber facultatif- la base des chiffres, un entier de 2 à 36
- number|nil
- le nombre, ou nil quand le texte n'est pas un nombre
Ce que renvoie read, ce que contient un fichier et ce que le shell passe dans arg est toujours du texte, et calculer avec du texte est une erreur ("10" + 1 s'arrête sur attempt to perform arithmetic on a string value). tonumber en fait un nombre, ou donne nil si le texte n'en est pas un, ce qui permet de vérifier.
Acceptés : des espaces autour, un signe, des décimales avec un point, un exposant, l'hexadécimal avec 0x. Refusés (nil) : tout autre caractère, un texte vide, une virgule décimale ("1,5", attention aux habitudes françaises), des mots. Les valeurs qui ne sont ni du texte ni des nombres (nil, true, une table) donnent aussi nil.
print(tonumber("42") + 1)
print(tonumber(" -3.5 "))
print(tonumber("1e3"), tonumber("0x1F"))
print(tonumber("12 pommes"), tonumber("1,5"))43 -3.5 1000 31 nil nil
Avec une base, le texte contient un entier écrit dans cette base, avec les lettres a à z (ou A à Z) pour les chiffres 10 à 35. Pas de préfixe 0x ni de décimales sous cette forme. La valeur doit alors être un texte (tonumber(42, 16) s'arrête sur bad argument #1 to 'tonumber' (string expected, got number)), et une base hors de 2 à 36 s'arrête sur bad argument #2 to 'tonumber' (base out of range).
print(tonumber("ff", 16), tonumber("1011", 2), tonumber("Z", 36))
print(tonumber("12", 2))255 11 35 nil
Demander un nombre au joueur. Reposez la question tant que la réponse n'est pas valable. Comme read donne du texte, tonumber vérifie que c'est un nombre, puis le programme vérifie qu'il est dans les bornes :
local function demander_nombre(question, bas, haut)
while true do
write(question .. " (" .. bas .. " à " .. haut .. ") : ")
local n = tonumber(read())
if n == nil then
print("Ce n'est pas un nombre.")
elseif n ~= math.floor(n) or n < bas or n > haut then
print("Un nombre entier de " .. bas .. " à " .. haut .. ", s'il vous plaît.")
else
return n
end
end
end
local vitesse = demander_nombre("Vitesse visée en tr/min", 0, 256)
print("Vitesse réglée à " .. vitesse .. " tr/min")tonumber(x) or 0 est un raccourci courant quand une valeur absente ou abîmée peut simplement compter pour zéro.
Voir aussi tostring() read()
Donne le type d'une valeur : "nil", "number", "string", "boolean", "table" ou "function".
valueany- n'importe quelle valeur,
nilcompris
- string
"nil","number","string","boolean","table"ou"function"
Servez-vous-en quand une valeur peut être de plusieurs sortes : des données reçues avec net.receive (du texte ou une table), le résultat d'une méthode d'appareil, un paramètre facultatif. Vos fonctions, les fonctions intégrées (print) et les itérateurs de pairs et ipairs sont tous des "function".
print(type(64), type("iron"), type(true))
print(type(nil), type({}), type(print))number string boolean nil table function
local function decrire(donnees)
if type(donnees) == "table" then
return "une table de " .. #table.keys(donnees) .. " entrées"
elseif type(donnees) == "number" then
return "le nombre " .. donnees
end
return tostring(donnees)
end
print(decrire({x = 10, z = -4}))
print(decrire(15))
print(decrire("bonjour"))une table de 2 entrées le nombre 15 bonjour
type() sans aucun argument arrête le programme avec bad argument #1 to 'type' (value expected) ; type(nil) est permis et donne "nil".
Voir aussi tostring()
Boucles
Une boucle for ... in a besoin de quelque chose à parcourir. pairs(t) visite toutes les clés d'une table, ipairs(t) la partie liste dans l'ordre. Une liste se parcourt aussi sans elles : for v in t do donne les valeurs t[1], t[2]... (voir Tables). Mais for k, v in t do, avec deux variables et sans pairs, s'arrête sur use pairs() or ipairs() to iterate a table with two variables.
Parcourt toutes les clés et valeurs d'une table : for k, v in pairs(t) do.
ttable- la table à parcourir
- function
- un itérateur, pour une boucle
for
L'ordre est toujours le même : d'abord les entiers 1, 2, 3... dans l'ordre, puis les autres clés dans l'ordre où elles ont été ajoutées. Contrairement à Lua, où l'ordre est aléatoire, vous pouvez compter dessus, par exemple pour afficher une table dans l'ordre où vous l'avez écrite. Les clés dont la valeur est nil n'existent pas et ne sont pas visitées.
local stock = {fer = 1200, cuivre = 640, zinc = 96}
stock.laiton = 32
for objet, nombre in pairs(stock) do
print(objet, nombre)
endfer 1200 cuivre 640 zinc 96 laiton 32
local t = {"premier", "deuxième", mode = "auto"}
t[3] = "troisième"
for k, v in pairs(t) do
print(k, v)
end1 premier 2 deuxième 3 troisième mode auto
Modifier la table pendant la boucle. Changer la valeur d'une clé, ou retirer une clé (la mettre à nil), ne pose aucun problème : une clé retirée qui n'a pas encore été visitée est sautée. Voici comment vider une table de ses tâches finies :
local taches = {presse = "finie", mixeur = "en cours", scie = "finie"}
for nom, etat in pairs(taches) do
if etat == "finie" then
taches[nom] = nil
end
end
print(#table.keys(taches) .. " tâche restante")1 tâche restante
Ajouter des clés pendant la boucle, c'est autre chose : certaines seront peut-être visitées, d'autres non, et ajouter au bout de la liste qu'on parcourt peut rendre la boucle infinie. Rangez ce qu'il faut ajouter dans une autre table, et ajoutez-le après la boucle.
La valeur que renvoie pairs est un itérateur : il ne marche que dans une boucle for. L'appeler vous-même s'arrête sur an iterator can only be used in a 'for' loop. Une valeur qui n'est pas une table arrête le programme : pairs(nil) donne bad argument #1 to 'pairs' (table expected, got no value), en général parce qu'une fonction a renvoyé nil là où vous attendiez une table.
Voir aussi ipairs() table.keys()
Parcourt t[1], t[2]... jusqu'au premier nil.
ttable- la liste à parcourir
- function
- un itérateur, pour une boucle
for
Il donne la position et la valeur, dans l'ordre, et ignore les clés nommées. Utilisez-le pour les listes : gares d'une ligne, emplacements, étapes d'une recette.
local arrets = {"Mine", "Fonderie", "Dépôt"}
arrets.ligne = "rouge"
for i, nom in ipairs(arrets) do
print(i .. ". " .. nom)
end1. Mine 2. Fonderie 3. Dépôt
Il s'arrête à la première position vide, même si des valeurs suivent. pairs les montrerait :
local cases = {"charbon", "fer", "or"}
cases[2] = nil
for i, objet in ipairs(cases) do print("ipairs", i, objet) end
for i, objet in pairs(cases) do print("pairs", i, objet) endipairs 1 charbon pairs 1 charbon pairs 3 or
La boucle lit t[i] à chaque tour : les changements faits pendant la boucle sont vus. Retirer des éléments avec table.remove en avançant saute l'élément qui glisse à la place retirée : parcourez plutôt la liste à l'envers avec un for numérique (voir table.remove).
Voir aussi pairs() table.insert()
Erreurs
Quand quelque chose tourne mal, le programme s'arrête et l'écran affiche en rouge le fichier, la ligne et le message : startup:12: attempt to index a nil value (local 'coffre'). Dans les exemples de cette page, le programme s'appelle snippet. Le guide Erreurs et débogage explique comment lire ces messages, et Messages d'erreur les liste.
error(message)
Arrête le programme avec une erreur.
messageany- le message (toute valeur est convertie en texte)
Utilisez-la quand le programme tombe sur une situation qu'il ne sait pas gérer : un appareil absent, un réglage hors limites. Le message reçoit le fichier et la ligne devant lui, le programme s'arrête et le shell revient, sauf si un pcall autour de l'appel attrape l'erreur.
local function regler_vitesse(rpm)
if rpm < -256 or rpm > 256 then
error("vitesse hors limites : " .. rpm)
end
print("vitesse réglée à " .. rpm)
end
regler_vitesse(128)
regler_vitesse(300)
print("jamais affiché")vitesse réglée à 128 snippet:3: vitesse hors limites : 300
Le message est toujours converti en texte avec tostring : error(42) donne 42, error() donne nil, et une table devient table: 0x... : une erreur ne peut donc pas transporter une table jusqu'au pcall qui l'attrape. Mettez les détails dans le texte. Il n'y a pas d'argument « niveau » comme en Lua : la position est toujours la ligne de l'appel à error, et un second argument est ignoré.
Appelle une fonction et attrape ses erreurs : vous obtenez {ok=true, value=...} ou {ok=false, error="..."}.
ffunction- la fonction à appeler
argumentsany facultatif- des valeurs passées à
f
- table
{ok = true, value = ...}ou{ok = false, error = "..."}
pcall(f, a, b) appelle f(a, b). Si elle va jusqu'au bout, vous obtenez une table avec ok = true et value, ce que f a renvoyé (absent si f n'a rien renvoyé). Si une erreur se produit n'importe où dedans, le programme ne s'arrête pas : vous obtenez ok = false et error, le message sous forme de texte, fichier et ligne compris. Comme une fonction Brass ne renvoie qu'une valeur, pcall donne une table là où Lua donne deux valeurs.
local function diviser(a, b)
if b == 0 then
error("division par zéro")
end
return a / b
end
local r = pcall(diviser, 10, 4)
print(r.ok, r.value)
r = pcall(diviser, 1, 0)
print(r.ok, r.error)true 2.5 false snippet:3: division par zéro
Ce qu'il attrape : toutes les erreurs levées pendant que f tourne, dans son propre code et dans tout ce qu'elle appelle. error et assert, un mauvais argument donné à une fonction de bibliothèque, un calcul sur nil, un appareil qui a été cassé, un disque plein, out of memory, stack overflow, un import qui échoue. Il marche aussi autour de fonctions qui attendent : f peut appeler sleep, read ou os.pull_event.
Ce qu'il ne peut pas attraper : Ctrl+T et le bouton Stopper, qui arrêtent toujours le programme, ni os.reboot et os.shutdown. Une faute de syntaxe dans le programme lui-même ne s'attrape pas non plus : le programme ne démarre jamais.
pcall sur autre chose qu'une fonction n'arrête pas le programme : pcall(nil) donne {ok = false, error = "attempt to call a nil value"}, sans numéro de ligne. Pratique pour appeler une méthode qu'un appareil n'a peut-être pas.
Charger un fichier de réglages sans risque. Les réglages d'une ligne de presses sont dans un fichier que le joueur peut modifier. S'il manque ou s'il est faux, le programme garde ses réglages par défaut au lieu de planter :
fs.write("presse.cfg", "vitesse = rapide\ncote = back\n") -- une faute dans le fichier
local function charger_config(chemin)
local texte = fs.read(chemin)
if texte == nil then
error("pas de fichier " .. chemin)
end
local config = {}
for _, ligne in ipairs(texte:split("\n")) do
local egal = ligne:find("=")
if egal then
local cle = ligne:sub(1, egal - 1):trim()
local valeur = ligne:sub(egal + 1):trim()
config[cle] = tonumber(valeur) or valeur
end
end
if type(config.vitesse) ~= "number" then
error("vitesse invalide : '" .. tostring(config.vitesse) .. "'")
end
return config
end
local config = {vitesse = 32, cote = "back"} -- si le fichier est mauvais
local r = pcall(charger_config, "presse.cfg")
if r.ok then
config = r.value
else
print("presse.cfg ignoré :")
print(r.error)
end
print("Presse à " .. config.vitesse .. " tr/min, " .. config.cote)presse.cfg ignoré : snippet:18: vitesse invalide : 'rapide' Presse à 32 tr/min, back
Utilisez pcall autour de ce qui peut échouer pour des raisons extérieures à votre programme : un bloc qu'on peut casser, un fichier que les joueurs modifient, un message venu d'un autre ordinateur. N'enveloppez pas tout : une erreur cachée est un bug que vous ne verrez pas.
Arrête le programme avec une erreur si la valeur est false ou nil ; sinon la renvoie.
valueany- la valeur à vérifier
messageany facultatif- le message d'erreur,
"assertion failed!"s'il est omis
- any
valueelle-même, quand elle n'est nifalseninil
assert(condition, "message") est un if not condition then error("message") end en une ligne. Comme il renvoie la valeur vérifiée, il peut vérifier et garder une valeur d'un seul coup : local texte = assert(fs.read("recettes"), "pas de fichier de recettes").
local coffre = {size = 27}
local cases = assert(coffre.size, "le coffre n'a pas de taille")
print("emplacements : " .. cases)
assert(cases > 30, "trop petit : " .. cases .. " emplacements")emplacements : 27 snippet:4: trop petit : 27 emplacements
Le message est construit avant qu'assert ne s'exécute, même quand tout va bien. assert(nombre, "mauvais nombre : " .. nombre) échoue sur le .. quand nombre vaut nil, avec attempt to concatenate a nil value au lieu de votre message.
Autres fichiers
Un programme peut se découper en plusieurs fichiers : une bibliothèque d'outils partagée par plusieurs programmes, les réglages d'une machine, le code de dessin d'un tableau de commande. Le guide Programmes en plusieurs fichiers montre comment les organiser, et Un projet en plusieurs fichiers en est un exemple complet.
Exécute un autre fichier une seule fois et renvoie ce qu'il renvoie : import "lib/outils".
pathstring- le fichier à exécuter, relatif au dossier du fichier qui importe, ou depuis la racine avec un
/au début
- any
- ce que le fichier renvoie avec
return,nils'il ne renvoie rien
Le fichier importé est un programme Brass ordinaire. Le plus souvent, il se termine par return { ... }, une table de fonctions :
-- outils partagés par les programmes de l'entrepôt
local function piles(nombre)
return math.ceil(nombre / 64)
end
local function barre(nombre, capacite, largeur)
local plein = math.min(largeur, math.floor(nombre / capacite * largeur))
return "[" .. string.rep("#", plein) .. string.rep(".", largeur - plein) .. "]"
end
return {piles = piles, barre = barre}local stock = import "lib/stock"
print(stock.piles(1000) .. " piles")
print(stock.barre(1000, 2048, 20))Avec un texte entre guillemets, les parenthèses sont facultatives : import "lib/stock" vaut import("lib/stock").
Où le fichier est cherché. Par rapport au dossier du fichier qui appelle import, pas au dossier courant du shell : jeux/serpent/main qui appelle import "dessin" charge jeux/serpent/dessin. .. remonte d'un dossier et un / au début part de la racine : import "../lib/stock", import "/lib/stock". À l'invite brass, le chemin est relatif au dossier courant. Le nom est pris tel quel : aucune extension n'est ajoutée, import "outils" charge le fichier outils, pas outils.lua.
Une fois par programme. Le premier import d'un fichier l'exécute. Tous les import suivants du même fichier, depuis n'importe quel fichier du programme, rendent la même valeur sans le relancer. Tous les fichiers partagent donc un seul exemplaire d'une bibliothèque, avec ses variables. Le lancement suivant du programme repart de zéro. Un fichier dont l'exécution s'est arrêtée sur une erreur n'est pas retenu : l'importer à nouveau le relance.
Ce qui est partagé. Les globales (fonctions et variables définies sans local) sont vues par tous les fichiers du programme. Les local du niveau du fichier lui restent privées. Préférez les locales et une table renvoyée : deux bibliothèques ne peuvent alors pas écraser les noms l'une de l'autre.
Cet exemple écrit une petite bibliothèque avec fs.write, puis l'importe deux fois :
fs.write("lib/unites", "chargements = (chargements or 0) + 1\n"
.. "local par_pile = 64\n"
.. "return {piles = function(n) return math.ceil(n / par_pile) end}\n")
local unites = import "lib/unites"
local encore = import "lib/unites"
print(unites.piles(200), chargements, unites == encore, par_pile)4 1 true nil
unites.piles marche, le fichier n'a tourné qu'une fois (chargements vaut 1, c'est une globale), les deux imports ont donné la même table, et la locale par_pile est restée dans la bibliothèque.
Quand il échoue, import arrête le programme avec une erreur que pcall peut attraper :
| message | cause |
|---|---|
cannot import 'nom': no such file | aucun fichier à ce chemin (vérifiez le dossier de référence) |
cannot import 'nom': it is a folder | le chemin désigne un dossier |
lib/stock:4: ... | une erreur dans le fichier importé, avec son propre nom et sa ligne |
circular import: 'a' is still being imported | a importe b, qui importe a à son tour : mettez le code commun dans un troisième fichier |
no storage medium | l'ordinateur n'a pas de disque |
Un fichier de réglages peut lui aussi être un fichier Brass, return {vitesse = 64, cote = "back"}, chargé avec une solution de repli :
local r = pcall(import, "/reglages")
local reglages = {vitesse = 32, cote = "back"}
if r.ok and type(r.value) == "table" then
reglages = r.value
endVoir aussi require() Programmes en plusieurs fichiers
Comme import.
pathstring- le fichier à exécuter, exactement comme pour
import
- any
- ce que le fichier renvoie
require est exactement la même fonction qu'import, pour les habitudes des joueurs de ComputerCraft et de Lua. Elle partage son cache : un fichier chargé avec l'une puis avec l'autre ne s'exécute qu'une fois. Contrairement à Lua, le chemin s'écrit avec des barres obliques, pas des points : require "lib/outils", pas require "lib.outils" (qui chercherait un fichier nommé lib.outils).
local outils = require "lib/outils"Voir aussi import()
Arguments du programme
Les mots tapés après le nom du programme à l'invite, sous forme de liste de textes.
- table
- les mots tapés après le nom du programme,
arg[0]étant le fichier du programme
Taper impulsion back 3 à l'invite (ou run impulsion back 3) lance le fichier impulsion avec arg[1] = "back" et arg[2] = "3". #arg est le nombre de mots, et arg[0] le chemin du programme depuis la racine, sans la barre du début ("impulsion", ou "outils/impulsion" pour un programme rangé dans un dossier).
-- usage : impulsion <côté> [fois]
local cote = arg[1]
local fois = tonumber(arg[2]) or 1
if cote == nil then
print("Usage : " .. arg[0] .. " <côté> [fois]")
return
end
print("Impulsions sur " .. cote .. " : " .. fois .. " fois")
for i = 1, fois do
rs.set(cote, true)
sleep(0.5)
rs.set(cote, false)
sleep(0.5)
end> impulsion Usage : impulsion <côté> [fois] > impulsion back 3 Impulsions sur back : 3 fois
- Les mots sont coupés aux espaces, et les guillemets n'ont rien de spécial :
stock "Lingot de fer"donne trois mots,"Lingot,deetfer". - Ce sont toujours des textes : convertissez les nombres avec
tonumber. - Un programme lancé au démarrage (
startup) ne reçoit aucun mot :argvaut{}, avecarg[0] = "startup". argexiste pendant qu'un programme lancé depuis un fichier tourne. À l'invitebrass, il vautnil.- C'est une globale ordinaire : tous les fichiers du programme la voient, et le programme peut la modifier.