Область функции / переменной (передача по значению или по ссылке?) - PullRequest
30 голосов
/ 25 мая 2011

Я полностью сбит с толку Lua переменная область видимости и передача аргумента функции (значение или ссылка).

См. Код ниже:

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) 

Как таковое, это поведение меня полностью смущает.

  • Означает ли это, что переменные таблицы передаются в функцию по ссылке, а не по значению?

  • Как создание глобальной переменной конфликтует с уже определенной локальной переменной?

    • Почему modt может изменять таблицу, а moda не может изменять переменную?

Ответы [ 5 ]

42 голосов
/ 25 мая 2011

Вы правильно догадались, переменные таблицы передаются по ссылке.Цитирование Справочное руководство по Lua 5.1 :

В Lua существует восемь основных типов: nil, логический, число, строка, функция, пользовательские данные, нить и таблица.....

Таблицы, функции, потоки и (полные) значения пользовательских данных являются объектами: переменные на самом деле не содержат этих значений, только ссылки на них.Присваивание, передача параметров и возврат функций всегда манипулируют ссылками на такие значения;эти операции не подразумевают никакого вида копирования.

Таким образом, nil, логические значения, числа и строки передаются по значению.Это точно объясняет поведение, которое вы наблюдаете.

20 голосов
/ 25 мая 2011

Lua function, table, userdata и thread (сопрограммы) передаются по ссылке.Другие типы передаются по значению.Или, как некоторые люди любят это выражать;все типы передаются по значению, но function, table, userdata и thread являются ссылочными типами.

string также является видом ссылочного типа, но является неизменным, интернированнымкопировать при записи - он ведет себя как тип значения, но с лучшей производительностью.

Вот что происходит:

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

Возможно, это поможет взглянуть на то, почемукак они:

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 голосов
/ 02 декабря 2011

jA_cOp является правильным, когда он говорит, что «все типы передаются по значению, но функция, таблица, userdata и thread являются ссылочными типами».

Важна разница между этим и «таблицами передаются по ссылке».

В этом случае это не имеет значения,

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

Результат: и «передать таблицу по ссылке», и «передать таблицу по значению, но таблица является ссылочным типом»,то же самое: x теперь имеет свое поле foo, установленное на "bar".

Но для этой функции она имеет огромное значение

function modt_2(x)
  x = {}
end

В этом случае передача по ссылке приведет к аргументупереходя на пустую таблицу.Однако в «передаче по значению, но в качестве ссылочного типа» новая таблица будет локально связана с x, а аргумент останется неизменным.Если вы попробуете это в lua, вы обнаружите, что происходит второе (значения являются ссылками).

7 голосов
/ 25 мая 2011

Я не буду повторять то, что уже было сказано в ответах Bas Bossink и jA_cOp о ссылочных типах, но:

- поскольку это определение локально, оно не должно иметь func scope

Это неверно.Переменные в Lua лексически ограничены , что означает, что они определены в блоке кода и во всех его вложенных блоках.
Что local создает новую переменную, ограниченную блоком, в котором находится операторявляется блоком, являющимся либо телом функции, «уровнем отступа», либо файлом.

Это означает, что когда вы делаете ссылку на переменную, Lua будет «сканировать вверх», пока не найдет блоккода, в котором эта переменная объявлена ​​локальной, по умолчанию используется глобальная область, если такого объявления нет.

В этом случае a и t объявляются локальными, но объявление находится в глобальной области видимости,поэтому a и t являются глобальными;или, самое большее, они являются локальными по отношению к текущему файлу.

Затем они не объявляются local внутри функций, , но они объявляются как параметры, что имеет тот же эффект.Если бы они не были параметрами функции, любая ссылка внутри тела функции все равно ссылалась бы на переменные снаружи.

На lua-users.org есть Scope Tutorial с некоторыми примерами, которые могут помочь вам больше, чем моя попытка объяснить. Программирование в разделе Lua по теме также является хорошим чтением.

1 голос
/ 25 мая 2011

Означает ли это, что переменные таблицы передаются в функцию по ссылке, а не по значению?

Да.

Как конфликтует создание глобальной переменнойс уже определенной локальной переменной?

Это не так.Это может выглядеть так, потому что у вас есть глобальная переменная с именем t и вы передаете ее функции с аргументом с именем t, но два t отличаются.Если вы переименуете аргумент во что-то другое, например, g, q, результат будет точно таким же.modt(t) может изменять глобальную переменную t только потому, что вы передаете ее по ссылке.Например, если вы позвоните modt({}), глобальный t не будет затронут.

Почему modt может изменять таблицу, а moda не может изменять переменную?

Поскольку аргументы являются локальными.Наименование вашего аргумента a похоже на объявление локальной переменной с local a, за исключением того, что аргумент получает переданное значение, а обычная локальная переменная - нет.Если бы ваш аргумент назывался z (или не присутствовал вообще), тогда moda действительно изменил бы глобальный a.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...