Je suis complètement confus par Lua la portée et l'argument de fonction variable passant (valeur ou référence).
Voir le code ci-dessous:
local a = 9 -- since it's define local, should not have func scope
local t = {4,6} -- since it's define local, should not have func scope
function moda(a)
a = 10 -- creates a global var?
end
function modt(t)
t[1] = 7 -- create a global var?
t[2] = 8
end
moda(a)
modt(t)
print(a) -- print 9 (function does not modify the parent variable)
print(t[1]..t[2]) -- print 78 (some how modt is modifying the parent t var)
En tant que tel, ce comportement me confond complètement.
Est-ce à dire que les variables du tableau sont passées à la fonction par référence et non par valeur?
En quoi la création de variable globale est-elle en conflit avec la variable locale déjà définie?
modt
est-il encore en mesure de modifier la table moda
ne peut pas modifier la variable a?Vous l'avez deviné, les variables de table sont passées par référence. Citer Manuel de référence Lua 5.1 :
Il existe huit types de base dans Lua: nil, booléen, nombre, chaîne, fonction, données utilisateur, thread et table. ....
Les tables, les fonctions, les threads et les valeurs de données utilisateur (complètes) sont des objets: les variables ne contiennent pas réellement ces valeurs, seulement des références à celles-ci. L'affectation, le passage de paramètres et les retours de fonction manipulent toujours les références à ces valeurs; ces opérations n'impliquent aucun type de copie.
Donc nil, les booléens, les nombres et les chaînes sont passés par valeur. Cela explique exactement le comportement que vous observez.
Les types function
, table
, userdata
et thread
(coroutine) de Lua sont transmis par référence. Les autres types sont passés par valeur. Ou comme certains aiment le dire; tous les types sont passés par valeur, mais function
, table
, userdata
et thread
sont des types de référence.
string
est également une sorte de type de référence, mais il est immuable, interné et copie sur écriture - il se comporte comme un type de valeur, mais avec de meilleures performances.
Voici ce qui se passe:
local a = 9
local t = {4,6}
function moda(a)
a = 10 -- sets 'a', which is a local introduced in the parameter list
end
function modt(t)
t[1] = 7 -- modifies the table referred to by the local 't' introduced in the parameter list
t[2] = 8
end
Cela mettra peut-être les choses en perspective pour expliquer pourquoi les choses sont comme elles sont:
local a = 9
local t = {4,6}
function moda()
a = 10 -- modifies the upvalue 'a'
end
function modt()
t[1] = 7 -- modifies the table referred to by the upvalue 't'
t[2] = 8
end
-- 'moda' and 'modt' are closures already containing 'a' and 't',
-- so we don't have to pass any parameters to modify those variables
moda()
modt()
print(a) -- now print 10
print(t[1]..t[2]) -- still print 78
jA_cOp a raison quand il dit "tous les types sont passés par valeur, mais la fonction, la table, les données utilisateur et le thread sont des types de référence".
La différence entre cela et "les tableaux sont transmis par référence" est importante.
Dans ce cas, cela ne fait aucune différence,
function modt_1(x)
x.foo = "bar"
end
Résultat: "passer la table par référence" et "passer la table par valeur, mais la table est un type de référence" fera de même: x a désormais son champ foo réglé sur "bar".
Mais pour cette fonction, cela fait un monde de différence
function modt_2(x)
x = {}
end
Dans ce cas, le passage par référence entraînera la modification de l'argument dans la table vide. Cependant dans le "passage par valeur, mais c'est un type de référence", une nouvelle table sera localement liée à x, et l'argument restera inchangé. Si vous essayez ceci dans lua, vous constaterez que c'est la seconde (les valeurs sont des références) qui se produit.
Je ne répéterai pas ce qui a déjà été dit sur les réponses de Bas Bossink et jA_cOp sur les types de référence, mais:
- comme il définit localement, ne devrait pas avoir de portée func
Ceci est une erreur. Les variables dans Lua sont de portée lexicale , ce qui signifie qu'elles sont définies dans un bloc de code et tous ses blocs imbriqués.
Ce que local
fait est de créer une nouvelle variable qui est limitée au bloc où se trouve l'instruction, un bloc étant soit le corps d'une fonction, un "niveau d'indentation" ou un fichier.
Cela signifie que chaque fois que vous faites référence à une variable, Lua "balaye vers le haut" jusqu'à ce qu'il trouve un bloc de code dans lequel cette variable est déclarée locale, par défaut à portée globale s'il n'y a pas une telle déclaration.
Dans ce cas, a
et t
sont déclarés locaux mais la déclaration est de portée globale, donc a
et t
sont globaux; ou tout au plus, ils sont locaux dans le fichier actuel.
Ils ne sont alors pas redéclarés local
à l'intérieur des fonctions, mais ils sont déclarés comme paramètres, ce qui a le même effet. S'ils n'avaient pas été des paramètres de fonction, toute référence à l'intérieur des corps de fonction ferait toujours référence aux variables à l'extérieur.
Il y a un Scope Tutorial sur lua-users.org avec quelques exemples qui peuvent vous aider plus que ma tentative d'explication. Programmation dans la section de Lua sur le sujet est également une bonne lecture.
Est-ce à dire que les variables du tableau sont passées à la fonction par référence et non par valeur?
Oui.
En quoi la création de variable globale est-elle en conflit avec la variable locale déjà définie?
Ce n'est pas le cas. Cela peut apparaître de cette façon car vous avez une variable globale appelée t
et la passez à une fonction avec un argument appelé t
, mais les deux t
sont différents. Si vous renommez l'argument en quelque chose d'autre, e, g, q
, la sortie sera exactement la même. modt(t)
est capable de modifier la variable globale t
uniquement parce que vous la transmettez par référence. Si vous appelez modt({})
, par exemple, le global t
ne sera pas affecté.
Pourquoi modt est-il capable de modifier la table alors que moda n'est pas capable de modifier la variable a?
Parce que les arguments sont locaux. Nommer votre argument a
revient à déclarer une variable locale avec local a
sauf que l'argument reçoit évidemment la valeur transmise et pas une variable locale régulière. Si votre argument s'appelait z
(ou n'était pas présent du tout) alors moda
modifierait en effet le global a
.