Recherche…


Syntaxe

  • funcname = function (paramA, paramB, ...) body; return exprlist end - une fonction simple
  • fonction funcname (paramA, paramB, ...) body; return exprlist end - raccourci pour ci-dessus
  • funcname local = fonction (paramA, paramB, ...) body; return exprlist end - un lambda
  • funcname local; funcname = function (paramA, paramB, ...) body; return exprlist end - lambda qui peut faire des appels récursifs
  • fonction locale funcname (paramA, paramB, ...) body; return exprlist end - raccourci pour ci-dessus
  • funcname (paramA, paramB, ...) - appelle une fonction
  • local var = var ou "Default" - un paramètre par défaut
  • retourne nil, "messages d'erreur" - façon standard d'abandonner avec une erreur

Remarques

Les fonctions sont généralement définies avec la function a(b,c) ... end et rarement avec la définition d'une variable à une fonction anonyme ( a = function(a,b) ... end ). Le contraire est vrai lors du passage de fonctions en tant que paramètres, les fonctions anonymes sont principalement utilisées et les fonctions normales ne sont pas utilisées aussi souvent.

Définir une fonction

function add(a, b)
    return a + b
end
-- creates a function called add, which returns the sum of it's two arguments

Regardons la syntaxe. Tout d'abord, nous voyons un mot-clé de function . Eh bien, c'est assez descriptif. Ensuite, nous voyons l'identifiant add ; le nom. Nous voyons alors les arguments (a, b) qui peuvent être n'importe quoi, et ils sont locaux. Nous ne pouvons y accéder que dans le corps de la fonction. Passons à la fin, on voit ... enfin la end ! Et tout ce qui est entre les deux est le corps de la fonction; le code qui est exécuté quand il est appelé. Le mot-clé return est ce qui fait que la fonction donne des résultats utiles. Sans elle, la fonction ne renvoie rien, ce qui équivaut à renvoyer zéro. Cela peut bien sûr être utile pour les choses qui interagissent avec IO, par exemple:

function printHello(name)
    print("Hello, " .. name .. "!");
end 

Dans cette fonction, nous n'avons pas utilisé l'instruction return.

Les fonctions peuvent également renvoyer des valeurs de manière conditionnelle, ce qui signifie qu'une fonction a le choix de ne rien renvoyer (aucune). Ceci est démontré dans l'exemple suivant.

function add(a, b)
    if (a + b <= 100) then
        return a + b -- Returns a value
    else
        print("This function doesn't return values over 100!") -- Returns nil
    end
end

Il est également possible pour une fonction de renvoyer plusieurs valeurs séparées par des virgules, comme indiqué:

function doOperations(a, b)
    return a+b, a-b, a*b
end

added, subbed, multiplied = doOperations(4,2)

Les fonctions peuvent également être déclarées locales

do
    local function add(a, b) return a+b end
    print(add(1,2)) --> prints 3
end
print(add(2, 2)) --> exits with error, because 'add' is not defined here

Ils peuvent également être enregistrés dans des tableaux:

tab = {function(a,b) return a+b end}
(tab[1])(1, 2) --> returns 3

Appeler une fonction

Les fonctions ne sont utiles que si nous pouvons les appeler. Pour appeler une fonction, la syntaxe suivante est utilisée:

print("Hello, World!")

Nous appelons la fonction d' print . En utilisant l'argument "Hello, World" . Comme il est évident, cela affichera Hello, World dans le flux de sortie. La valeur renvoyée est accessible, comme toute autre variable serait.

local added = add(10, 50) -- 60

Les variables sont également acceptées dans les paramètres d'une fonction.

local a = 10
local b = 60

local c = add(a, b)

print(c)

Les fonctions qui attendent une table ou une chaîne peuvent être appelées avec un sucre syntaxique net: les parenthèses entourant l'appel peuvent être omises.

print"Hello, world!"
for k, v in pairs{"Hello, world!"} do print(k, v) end

Fonctions anonymes

Création de fonctions anonymes

Les fonctions anonymes sont comme les fonctions Lua ordinaires, sauf qu'elles n'ont pas de nom.

doThrice(function()
    print("Hello!")
end)

Comme vous pouvez le voir, la fonction n’est assignée à aucun nom comme print ou add . Pour créer une fonction anonyme, il vous suffit d'omettre le nom. Ces fonctions peuvent également prendre des arguments.

Comprendre le sucre syntaxique

Il est important de comprendre que le code suivant

function double(x)
    return x * 2
end

est en fait juste un raccourci pour

double = function(x)
    return x * 2
end

Cependant, la fonction ci-dessus n'est pas anonyme car la fonction est directement affectée à une variable!

Les fonctions sont des valeurs de première classe

Cela signifie qu'une fonction est une valeur avec les mêmes droits que les valeurs conventionnelles telles que les nombres et les chaînes. Les fonctions peuvent être stockées dans des variables, dans des tables, peuvent être passées en arguments et peuvent être renvoyées par d'autres fonctions.

Pour le démontrer, nous allons également créer une fonction "half":

half = function(x)
    return x / 2
end

Donc, maintenant nous avons deux variables, half et double , contenant toutes deux une fonction en tant que valeur. Que faire si nous voulions créer une fonction qui alimenterait le nombre 4 en deux fonctions données et calculer la somme des deux résultats?

Nous voudrons appeler cette fonction comme sumOfTwoFunctions(double, half, 4) . Cela alimentera la fonction double , la half fonction et le nombre entier 4 dans notre propre fonction.

function sumOfTwoFunctions(firstFunction, secondFunction, input)
    return firstFunction(input) + secondFunction(input)
end

La fonction sumOfTwoFunctions ci-dessus montre comment les fonctions peuvent être transmises aux arguments et accédées par un autre nom.

Paramètres par défaut

function sayHello(name)
    print("Hello, " .. name .. "!")
end

Cette fonction est une fonction simple et fonctionne bien. Mais que se passerait-il si on appelait seulement sayHello() ?

stdin:2: attempt to concatenate local 'name' (a nil value)
stack traceback:
    stdin:2: in function 'sayHello'
    stdin:1: in main chunk
    [C]: in ?

Ce n'est pas vraiment génial. Il y a deux manières de corriger cela:

  1. Vous revenez immédiatement de la fonction:

    function sayHello(name)
      if not (type(name) == "string") then
        return nil, "argument #1: expected string, got " .. type(name)
      end -- Bail out if there's no name.
      -- in lua it is a convention to return nil followed by an error message on error
    
      print("Hello, " .. name .. "!") -- Normal behavior if name exists.
    end
    
  2. Vous définissez un paramètre par défaut .

    Pour ce faire, utilisez simplement cette expression simple

function sayHello(name)
    name = name or "Jack" -- Jack is the default, 
                          -- but if the parameter name is given, 
                          -- name will be used instead
    print("Hello, " .. name .. "!")
end

Le name = name or "Jack" idiome name = name or "Jack" fonctionne parce que or dans les courts-circuits Lua. Si l'élément à gauche de or est différent de nil ou false , le côté droit n'est jamais évalué. D'un autre côté, si sayHello est appelé sans paramètre, le name sera nil et la chaîne "Jack" sera assignée à son name . (Notez donc que cet idiome ne fonctionnera pas si le booléen false est une valeur raisonnable pour le paramètre en question.)

Résultats multiples

Les fonctions dans Lua peuvent renvoyer plusieurs résultats.

Par exemple:

function triple(x)
    return x, x, x
end

Lors de l'appel d'une fonction, pour enregistrer ces valeurs, vous devez utiliser la syntaxe suivante:

local a, b, c = triple(5)

Ce qui se traduira par a = b = c = 5 dans ce cas. Il est également possible d'ignorer les valeurs renvoyées en utilisant la variable jetable _ à l'endroit souhaité dans une liste de variables:

local a, _, c = triple(5)

Dans ce cas, la deuxième valeur renvoyée sera ignorée. Il est également possible d'ignorer les valeurs de retour en ne les affectant à aucune variable:

local a = triple(5)

La variable a verra attribuer la première valeur de retour et les deux autres seront rejetées.

Lorsqu'une fonction renvoie une quantité variable de résultats, on peut tous les stocker dans une table, en exécutant la fonction à l'intérieur:

local results = {triple(5)}

De cette façon, on peut parcourir le tableau des results pour voir ce que la fonction a renvoyé.

Remarque

Cela peut être une surprise dans certains cas, par exemple:

local t = {}
table.insert(t, string.gsub("  hi", "^%s*(.*)$", "%1")) --> bad argument #2 to 'insert' (number expected, got string)

Cela se produit parce que string.gsub renvoie 2 valeurs: la chaîne donnée, les occurrences du motif remplacé et le nombre total de correspondances.

Pour résoudre ce problème, utilisez une variable intermédiaire ou mettez () autour de l'appel, comme ceci:

table.insert(t, (string.gsub("  hi", "^%s*(.*)$", "%1"))) --> works. t = {"hi"}

Ceci ne saisit que le premier résultat de l'appel et ignore le reste.

Nombre variable d'arguments

Arguments Variadic

Arguments nommés

local function A(name, age, hobby)
    print(name .. "is " .. age .. " years old and likes " .. hobby)
end
A("john", "eating", 23) --> prints 'john is eating years old and likes 23'
-- oops, seems we got the order of the arguments wrong...
-- this happens a lot, specially with long functions that take a lot of arguments
-- and where the order doesn't follow any particular logic

local function B(tab)
    print(tab.name .. "is " .. tab.age .. " years old and likes " .. tab.hobby)
end
local john = {name="john", hobby="golf", age="over 9000", comment="plays too much golf"}
B(john)
--> will print 'John is over 9000 years old and likes golf'
-- I also added a 'comment' argument just to show that excess arguments are ignored by the function

B({name = "tim"}) -- can also be written as
B{name = "tim"} -- to avoid cluttering the code
--> both will print 'tim is nil years old and likes nil'
-- remember to check for missing arguments and deal with them

function C(tab)
    if not tab.age then return nil, "age not defined" end
    tab.hobby = tab.hobby or "nothing"
    -- print stuff
end

-- note that if we later decide to do a 'person' class
-- we just need to make sure that this class has the three fields
-- age, hobby and name, and it will be compatible with these functions

-- example:
local john = ClassPerson.new("John", 20, "golf") -- some sort of constructor
john.address = "some place" -- modify the object
john:do_something("information") -- call some function of the object
C(john) -- this works because objects are *usually* implemented as tables

Vérification des types d'argument

Certaines fonctions ne fonctionnent que sur un certain type d'argument:

function foo(tab)
    return tab.bar
end
--> returns nil if tab has no field bar, which is acceptable
--> returns 'attempt to index a number value' if tab is, for example, 3
--> which is unacceptable

function kungfoo(tab)
    if type(tab) ~= "table" then
        return nil, "take your useless " .. type(tab) .." somewhere else!"
    end

    return tab.bar
end

cela a plusieurs implications:

print(kungfoo(20)) --> prints 'nil, take your useless number somewhere else!'

if kungfoo(20) then print "good" else print "bad" end --> prints bad

foo = kungfoo(20) or "bar" --> sets foo to "bar"

maintenant nous pouvons appeler la fonction avec n'importe quel paramètre que nous voulons, et cela ne plantera pas le programme.

-- if we actually WANT to abort execution on error, we can still do
result = assert(kungfoo({bar=20})) --> this will return 20
result = assert(kungfoo(20)) --> this will throw an error

Alors, que faire si nous avons une fonction qui fait quelque chose avec une instance d'une classe spécifique? C'est difficile, car les classes et les objets sont généralement des tables, de sorte que la fonction type renvoie 'table' .

local Class = {data="important"}
local meta = {__index=Class}

function Class.new()
    return setmetatable({}, meta)
end
-- this is just a very basic implementation of an object class in lua

object = Class.new()
fake = {}

print(type(object)), print(type(fake)) --> prints 'table' twice

Solution: comparez les métabalises

-- continuation of previous code snippet
Class.is_instance(tab)
    return getmetatable(tab) == meta
end

Class.is_instance(object) --> returns true
Class.is_instance(fake) --> returns false
Class.is_instance(Class) --> returns false
Class.is_instance("a string") --> returns false, doesn't crash the program
Class.is_instance(nil) --> also returns false, doesn't crash either

Fermetures

do
    local tab = {1, 2, 3}
    function closure()
        for key, value in ipairs(tab) do
            print(key, "I can still see you")
        end
    end
    closure()
    --> 1 I can still see you
    --> 2 I can still see you
    --> 3 I can still see you
end

print(tab) --> nil
-- tab is out of scope

closure()
--> 1 I can still see you
--> 2 I can still see you
--> 3 I can still see you
-- the function can still see tab

exemple d'utilisation typique

function new_adder(number)
    return function(input)
        return input + number
    end
end
add_3 = new_adder(3)
print(add_3(2)) --> prints 5

exemple d'utilisation plus avancé

function base64.newDecoder(str) -- Decoder factory
    if #str ~= 64 then return nil, "string must be 64 characters long!" end

    local tab = {}
    local counter = 0
    for c in str:gmatch"." do
        tab[string.byte(c)] = counter
        counter = counter + 1
    end

    return function(str)
        local result = ""

        for abcd in str:gmatch"..?.?.?" do
            local a, b, c, d = string.byte(abcd,1,-1)
            a, b, c, d = tab[a], tab[b] or 0, tab[c] or 0, tab[d] or 0
            result = result .. (
                string.char( ((a<<2)+(b>>4))%256 ) ..
                string.char( ((b<<4)+(c>>2))%256 ) ..
                string.char( ((c<<6)+d)%256 )
            )
        end
        return result
    end
end


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow