Fonction / portée variable (valeur de passage ou référence?)

je suis complètement confus par Lua's variable de délimitation de l'étendue et de la fonction argument de passage de la valeur (ou de 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.

  • cela signifie-t-il que les variables de la table sont passés à la fonction par de référence et non pas de la valeur?

  • Comment est la création de la variable globale en conflit avec le déjà définir local variable?

    • Pourquoi modt mesure de modifier la table moda est incapable pour modifier la variable?
30
demandé sur Uwe Keim 2011-05-25 21:03:09

5 réponses

vous avez deviné à droite, les variables de table sont passées par référence. Citant Lua 5.1 Manuel De Référence:

Il y a huit types de base en Lua: néant, booléen, nombre, chaîne de caractères, la fonction, userdata, le fil, et de la table. ....

des Tables, des fonctions, des fils, et (complet) userdata les valeurs sont des objets: les variables ne contient pas ces valeurs, seules les références. L'affectation, le passage de paramètres et les retours de fonction manipulent toujours les références à de telles valeurs; ces opérations n'impliquent aucune sorte de copie.

donc nil, booléens, nombres et chaînes sont passés par valeur. Cela explique exactement le comportement que vous observez.

40
répondu Bas Bossink 2011-05-25 17:16:49

Lua function, table,userdata et thread (coroutine) les types sont passés par référence. Les autres types sont passés par valeur. Ou comme certains aiment à le mettre, tous les types sont passés par valeur, mais function, table,userdata et thread sont des types de référence.

string est aussi une sorte de type de référence, mais est immuable, interné et copy-on-write - il se comporte comme un type de valeur, mais avec de meilleures performances.

Voici ce que 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

peut-être que ceci mettra les choses en perspective quant à la raison pour laquelle 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
19
répondu jA_cOp 2011-05-25 17:30:04

jA_cOp a raison quand il dit "tous les types sont passés par la valeur, mais la fonction, la table, userdata et le thread sont des types de référence."

la différence entre ceci et "les tableaux sont passés par référence" est importante.

Dans ce cas, il ne fait aucune différence,

function modt_1(x)
  x.foo = "bar"
end

résultat: les deux " pass table by reference "et" pass table by value, but table is a reference type "vont faire la même chose: x a maintenant son champ foo défini à"bar".

Mais ce fonction il fait un monde de différence

function modt_2(x)
  x = {}
end

Dans ce cas passer par référence entraînera l'argument se changer à la table vide. Cependant dans la "valeur de passage, mais c'est un type de référence", une nouvelle table sera liée localement à x, et l'argument restera inchangé. Si vous essayez ceci dans lua, vous trouverez que c'est la deuxième (valeurs de référence) se produit.

19
répondu Michael Anderson 2011-12-02 08:31:07

Je ne vais pas répéter 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:

-- puisqu'il est défini local, ne devrait pas avoir la portée func

Ceci est incorrect. Les Variables en Lua sont correspondant a une portée lexicale, ce qui signifie qu'ils sont définis dans un bloc de code et tous ses blocs imbriqués.

local n'est de créer une nouvelle variable est limitée au bloc où l'énoncé n'est pas un bloc soit le corps d'une fonction, d'un "niveau d'indentation" ou un fichier.

cela signifie que chaque fois que vous faites une référence à une variable, Lua va "scanner vers le haut" jusqu'à ce qu'il trouve un bloc de code dans lequel cette variable est déclarée locale, en sautant à la portée globale s'il n'y a pas de telle déclaration.

Dans ce cas, a et t sont déclarés locaux mais la déclaration est de portée mondiale, donc a et t sont mondiales; ou tout au plus sont locales à la fichier en cours.

ils ne sont donc pas non déclarés!--0--> à l'intérieur de l'fonctions, mais ils sont déclarés comme paramètres, 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 se référerait encore aux variables à l'extérieur.

il y a un Tutoriel Sur La Portée 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.

7
répondu Zecc 2011-05-25 19:07:07

cela signifie-t-il que les variables de table sont passées à la fonction par référence et non par valeur?

Oui.

comment la création d'une variable globale entre-t-elle en conflit avec la variable locale déjà définie?

ce n'est pas le cas. Cela pourrait se produire ainsi parce que vous avez une variable globale appelée t et passer à une fonction avec un argument appelé t, mais les deux ts sont différents. Si vous renommez le argument à autre chose, e,g, q la sortie sera exactement la même. modt(t) est capable de modifier la variable globale t seulement parce que vous le passez par référence. Si vous appelez modt({}), par exemple, le global t ne sera pas affectée.

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 est similaire à déclarer un local variable avec local a sauf qu'évidemment l'argument reçoit la valeur transmise et pas une variable locale régulière. Si votre argument a été appelé z (ou n'était pas présent), alors moda modifierait en effet lea.

1
répondu finnw 2011-05-25 19:20:46