web-dev-qa-db-fra.com

Fonction / portée variable (passer par valeur ou référence?)

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?

    • Pourquoi modt est-il encore en mesure de modifier la table moda ne peut pas modifier la variable a?
31
frooyo

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.

43
Bas Bossink

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
20
jA_cOp

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.

19
Michael Anderson

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.

7
Zecc

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.

1
finnw