Добавлен Алексей Андреич,
опубликован
Руководство по Lua 5.1
Содержание:
До этого момента мы просто назначали значения переменным и могли получить эти значения, обратившись по имени переменной в скрипте. Это довольно удобно для маленьких проектов, но теперь, когда вы ознакомились с функциями, это может создать некоторые проблемы: а что будет, если функции будут использовать переменные с одинаковыми именами для хранения временных значений? Они просто будут переписывать друг друга, а скрипт превратится в невозможное для отладки месиво. Решение есть: использовать локальные переменные.
Создание локальной переменной
Для создания локальной переменной укажите ключевое слово local перед именем:
local a = 5
print(a)
Для изменения значения локальной переменной не требуется указывать ключевое слово local
local a = 5
a = 6
Локальные переменные существуют только в пределах блоков, в которых были созданы, за их пределами они больше не существуют:
local a = 5
print(a) --> 5
do
local a = 6 -- создаём переменную а внутри блока do
print(a) --> 6
end
print(a) --> 5
Граница, где существует локальная переменная, где она "видима", называется областью видимости.
function bar()
print(x) --> nil
local x = 6
print(x) --> 6
end
function foo()
local x = 5
print(x) --> 5
bar()
print(x) --> 5
end
foo()
Как вы видите, локальные переменные видимы только внутри блоков, где они были объявлены. Пусть даже локальные переменные имеют одинаковое название, они не зависят друг от друга.
Локальные функции - синтаксический сахар
local function f() end
-- равно
local f
f = function() end
-- но не равно
local f = function() end
Разница между последними двумя вариантами весьма велика : первые два варианта создадут локальные переменные, а третий создаст глобальную.
Замыкания
Функции могут использовать локальные переменные, созданные вне их пределов . Такие функции называются замыканиями.
Начнём с простого примера. Допустим, у вас есть список имён учеников и таблица, в которой соотносятся имена с классов учеников. Вы хотите отсортировать список имён в соответствии с классами (от большего к меньшему). Можно сделать так:
names = {"Peter", "Paul", "Mary"}
grades = {Mary = 10, Paul = 7, Peter = 8}
table.sort(names, function (n1, n2)
return grades[n1] > grades[n2] -- сравниваем классы
end)
А теперь, допустим, вы хотите создать функцию для этой задачи:
function sortbygrade (names, grades)
table.sort(names, function (n1, n2)
return grades[n1] > grades[n2] -- compare the grades
end)
end
Интересно здесь то, что безымянная функция, переданная в функцию sort, получается доступ к аргументу grades, являющийся локальным для функции sortbygrade. Внутри безымянной функции grades не является ни локальной, ни глобальной переменной. Это, так называемая, внешняя локальная переменная (Далее ВЛП)
Рассмотрим следующий код:
function newCounter ()
local i = 0
return function () -- безымянная функция
i = i + 1
return i
end
end
c1 = newCounter()
print(c1()) --> 1
print(c1()) --> 2
Теперь безымянная функция использует ВЛП i для счёта. Однако, когда мы вызываем безымянную функцию, i уже вне области видимости, т.к. newCounter уже вернула безымянную функцию. Тем не менее Lua справляется с такими ситуациями, используя замыкания.
Если мы снова вызовем newCounter, создастся новая локальная переменная i и, соответственно, новое замыкание:
Если мы снова вызовем newCounter, создастся новая локальная переменная i и, соответственно, новое замыкание:
c2 = newCounter()
print(c2()) --> 1
print(c1()) --> 3
print(c2()) --> 2
Также эффективная связка замыкания+функции обратного вызова.
Рассмотрим такую ситуацию: вы делаете интерфейс для цифрового калькулятора, и вы хотите сделать несколько разных кнопок, выполняющие немного разные функции при нажатии. Можно создать эти функции так:
Рассмотрим такую ситуацию: вы делаете интерфейс для цифрового калькулятора, и вы хотите сделать несколько разных кнопок, выполняющие немного разные функции при нажатии. Можно создать эти функции так:
function digitButton (digit)
return Button{ label = digit,
action = function ()
add_to_display(digit)
end
}
end
В этом примере мы расскатриваеи Button как инструментарий функции для создания новой кнопки.
label обозначение кнопки; action функция обратного вызова при нажатии. (В действительности это замыкание, т.к. имеет доступ к ВЛП digit). Функция обратного вызова может быть вызвана и через некоторое время после того, как digitButton выполнит свою задачу а локальная переменная digit выйдет из области видимости. но всё равно она будет иметь доступ к этой переменной.
=== Когда стоит использовать локальные переменные? ===
label обозначение кнопки; action функция обратного вызова при нажатии. (В действительности это замыкание, т.к. имеет доступ к ВЛП digit). Функция обратного вызова может быть вызвана и через некоторое время после того, как digitButton выполнит свою задачу а локальная переменная digit выйдет из области видимости. но всё равно она будет иметь доступ к этой переменной.
=== Когда стоит использовать локальные переменные? ===
Основное правило: всегда используйте локальные переменные, за исключением тех случаев, где необходимо, что бы они были глобальными.
Так как можно забыть указать ключевое слово local и сам Lua никак не предупреждает об этом, возможно появления багов. Одним из решений данной проблемы является использование скрипта
"strict.lua" (написан ниже). Скопируйте скрипт себе в проект и укажите в главном файле строку:
require("strict")
"strict.lua" (написан ниже). Скопируйте скрипт себе в проект и укажите в главном файле строку:
require("strict")
-- strict.lua
-- отслеживает использование необъявленных глобальных переменных
-- Все глобальные переменные должны быть инициализированы во время объявления
-- (можно даже установить значение nil)
local mt = getmetatable(_G)
if mt == nil then
mt = {}
setmetatable(_G, mt)
end
__STRICT = true
mt.__declared = {}
mt.__newindex = function (t, n, v)
if __STRICT and not mt.__declared[n] then
local w = debug.getinfo(2, "S").what
if w ~= "main" and w ~= "C" then
error("assign to undeclared variable '"..n.."'", 2)
end
mt.__declared[n] = true
end
rawset(t, n, v)
end
mt.__index = function (t, n)
if not mt.__declared[n] and debug.getinfo(2, "S").what ~= "C" then
error("variable '"..n.."' is not declared", 2)
end
return rawget(t, n)
end
function global(...)
for _, v in ipairs{...} do mt.__declared[v] = true end
end
данная статья является вольным переводом с сайта lua-users.org
с возможными дополнениями и изменениями
с возможными дополнениями и изменениями
Содержание
`
ОЖИДАНИЕ РЕКЛАМЫ...
0
prog
11 лет назад
0
Название статьи поправь - "области видимости", а не просто "области". Еще лучше - "локальные переменные" т.к. о самих областях видимости сказано мало.
Замыкания как-то не очень внятно описаны - им можно целую статью посвятить, а тут пара абзацев и не совсем точно. О замыканиях на lua.org:
http://www.lua.org/pil/6.1.html
0
Алексей Андреич
11 лет назад
0
prog:
ок, позже поправлю и добавлю больше инфы о замыканиях...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.