2011-05-25 15 views
29

Estoy completamente confundido por Lua variable del alcance y el argumento de la función que pasa (valor o referencia).Ámbito de funciones/variables (¿pasar por valor o referencia?)

ver el código de abajo:

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) 

Como tal, este comportamiento me confunde por completo.

  • ¿Esto significa que las variables de tabla se pasan a la función por referencia y no valoran?

  • ¿Cómo está la creación de variable global en conflicto con la variable local que ya está definida?

    • ¿Por qué es capaz de modt modificar la tabla todavía moda no es capaz de modificar la una variable?

Respuesta

37

acertaste, las variables de tabla se pasan por referencia. Citando Lua 5.1 Reference Manual:

Hay ocho tipos básicos en Lua: nulo, booleano, número, cadena, función, datos de usuario, hilo y tabla. ....

Las tablas, funciones, subprocesos y los valores (completos) de los datos de usuario son objetos: las variables en realidad no contienen estos valores, solo las referencias a ellos. La asignación, el paso de parámetros y los retornos de funciones siempre manipulan referencias a tales valores; estas operaciones no implican ningún tipo de copia.

Así nil, booleanos, números y cadenas se pasan por valor. Esto explica exactamente el comportamiento que observas.

+4

Esto es sutilmente diferente de pasar por referencia. (Ver mi respuesta). En particular, el comportamiento de 'function (x) x = {} end' es diferente. –

+1

Todo pasa por valor, por ciertos tipos (tablas, funciones, subprocesos y valores de usuario completos) son referencias. Estas referencias se pasan por valor. – Ethan

18

function, table, userdata y thread (co-rutina) tipos de Lua se pasan por referencia. Los otros tipos se pasan por valor. O como a algunas personas les gusta decirlo; todos los tipos se pasan por valor, pero function, table, userdata y thread son tipos de referencia.

string es también un tipo de tipo de referencia, pero es inmutable, interno y copy-on-write; se comporta como un tipo de valor, pero con un mejor rendimiento.

Esto es lo que está pasando:

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 

Tal vez esto hará que las cosas en perspectiva de por qué las cosas son como son:

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 
7

No voy a repetir lo que ya se ha dicho en las respuestas Bas Bossink y de jA_cOp sobre los tipos de referencia, pero:

- ya que es definir local, no debería tener un alcance func

Esto es incorrecto. Las variables en Lua son lexically scoped, lo que significa que están definidas en un bloque de código y todos sus bloques anidados.
Lo que local hace es crear una nueva variable que está limitada al bloque donde está la instrucción, un bloque que es el cuerpo de una función, un "nivel de sangría" o un archivo.

Esto significa que siempre que haga una referencia a una variable, Lua "escaneará hacia arriba" hasta que encuentre un bloque de código en el que esa variable se declara local, por defecto a alcance global si no existe tal declaración.

En este caso, a y t sean declaradas local, pero la declaración es de alcance mundial, por lo a y t son globales; o a lo sumo, son locales al archivo actual.

No se vuelven a declarar local dentro de las funciones, pero se declaran como parámetros, lo que tiene el mismo efecto. Si no hubieran sido parámetros de función, cualquier referencia dentro de los cuerpos de función todavía se referiría a las variables externas.

Hay un Scope Tutorial en lua-users.org con algunos ejemplos que pueden ayudarlo más que mi intento de explicación. Programming in Lua's section on the subject es también una buena lectura.

1

¿Esto significa que las variables de tabla se pasan a la función por referencia y no por valor?

Sí.

¿Cómo está la creación de la variable global en conflicto con la variable local ya definida?

No lo es. Puede parecer de esa manera porque tiene una variable global llamada t y la pasa a una función con un argumento llamado t, pero los dos t s son diferentes. Si cambia el nombre del argumento a otra cosa, e, g, q, la salida será exactamente la misma. modt(t) puede modificar la variable global t solo porque la está pasando por referencia. Si llama al modt({}), por ejemplo, el global t no se verá afectado.

¿Por qué es posible modificar la tabla pero la moda no puede modificar la variable?

Porque los argumentos son locales. Nombrar su argumento a es similar a declarar una variable local con local a, excepto obviamente que el argumento recibe el valor transferido y una variable local regular no lo hace. Si su argumento se llamó z (o no estaba presente en absoluto) entonces moda efectivamente modificaría el a global.

16

jA_cOp es correcto cuando dice "todos los tipos se pasan por valor, pero función, tabla, datos de usuario e hilo son tipos de referencia".

La diferencia entre esto y "las tablas se pasan por referencia" es importante.

En este caso no hace ninguna diferencia,

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

Resultado: tanto "pasar a la mesa por referencia" y "pasar a la mesa por valor, pero la mesa es un tipo de referencia" hará lo mismo: x tiene ahora su campo foo establecido en "barra".

Pero para esta función se hace un mundo de diferencia

function modt_2(x) 
    x = {} 
end 

En este caso pase por referencia dará lugar a la discusión cambiándose a la mesa vacía. Sin embargo, en el "valor de paso, pero es un tipo de referencia", una nueva tabla se vinculará localmente a x, y el argumento permanecerá sin cambios. Si prueba esto en lua, encontrará que es el segundo (los valores son referencias) lo que ocurre.

+0

esto es realmente útil, gracias –

+0

He encontrado una buena manera de pensar acerca de esto es solo que todo se pasa por valor. Sin embargo, sucede que algunos tipos son solo referencias. Las referencias mismas se pasan por valor, razón por la cual su ejemplo no altera 't'. Buena explicación :) – Ethan

Cuestiones relacionadas