Локальные и глобальные переменные
С урока про переменные вы пишете local перед каждой переменной, и мы обещали объяснить, зачем. Теперь, когда вы пишете функции, пришло время.
У каждой локальной переменной есть область видимости: часть программы, где она существует. Область видимости начинается со строки, в которой переменная создана, и заканчивается вместе с блоком, в котором она создана: телом функции, блоком if или цикла либо всей программой. Параметры — это локальные переменные своей функции. Поэтому переменная, созданная внутри функции, снаружи не существует:
local function countEggs()
local eggs = 12
print("В сарае: " .. eggs)
end
countEggs()
print(eggs)Программа выводит:
В сарае: 12
nilСнаружи функции никакой eggs нет, поэтому последняя строка выводит nil. Каждый вызов функции создаёт её локальные переменные заново, и они исчезают, когда функция заканчивается. Это удобно: у двух функций может быть своя переменная total, и они никогда не помешают друг другу.
И наоборот: функция видит локальные переменные, созданные выше неё, снаружи, и может даже менять их:
local coins = 10
local function buySeeds(price)
coins = coins - price
end
buySeeds(3)
buySeeds(2)
print(coins)Программа выводит:
5А что с переменной без local? coins = 50 создаёт глобальную переменную. Глобальная переменная не принадлежит никакому блоку: с момента создания она существует везде, в любой функции, до конца программы. Встроенные print, math и table в Lua — тоже глобальные переменные; поэтому их можно использовать где угодно. Если Lua не находит локальную переменную с таким именем, он ищет глобальную, а если нет и её, значение — nil. Вот почему вызов функции слишком рано давал ошибку attempt to call a nil value (global 'morningChores').
Глобальные переменные кажутся удобными, но порождают баги, которые трудно найти:
- Опечатка создаёт новую переменную. Поскольку переменную можно создать без
local, Lua не отличает опечатку от новой переменной.cions = coins + 5— не ошибка: Lua тихо создаёт глобальную переменнуюcions, аcoinsне меняется. - Изменить их может что угодно. Любая функция в любом месте программы может изменить глобальную переменную, поэтому, когда её значение неверно, приходится искать по всей программе. Даже
printможно перезаписать: послеprint = 5каждыйprint(…)завершается ошибкойattempt to call a number value (global 'print').
Хуже всего — забытый local внутри функции:
local function sellEggs(eggs)
coins = eggs * 2
return coins
end
coins = 50
local earned = sellEggs(10)
print("Заработано: " .. earned)
print("Монеты: " .. coins)Программа выводит:
Заработано: 20
Монеты: 20У Эммы было 50 монет, а после продажи яиц стало 20? Функции нужна была своя переменная, но без local переменная coins внутри функции — это та же глобальная переменная, что и снаружи, и функция её перезаписала. С local coins = eggs * 2 в функции это были бы две разные переменные, и монеты Эммы остались бы при ней.
Поэтому в этом курсе local пишут перед каждой переменной и каждой функцией (function sellEggs без local создаёт глобальную функцию). Тогда каждая переменная существует только там, где она нужна, и никакая функция не изменит чужие переменные случайно. Значения попадают в функцию через параметры, а выходят с помощью return. В придачу Lua находит локальные переменные быстрее глобальных. Только помните, что область видимости начинается со строки с local: функция может использовать только те локальные переменные и локальные функции, которые созданы выше неё.
Задание
- 1
Куры Эммы несли яйца каждый день, но программа выводит
Яиц за неделю: 0. ВcountEggsв имени переменной опечатка. Lua о ней не сообщил: он тихо создал глобальную переменную. Найдите опечатку и исправьте её. Тогда программа выведетЯиц за неделю: 49. - 2
Теперь программа выводит
Заработано монет: 98иМонеты: 196. Но у Эммы было 50 монет, так что у неё должно стать 148. У переменнойcoinsвsellEggsнетlocal, поэтому функция перезаписываетcoinsЭммы из начала программы. Исправьте функцию, чтобы она использовала собственную локальную переменную. Тогда программа выводит:Яиц за неделю: 49 Заработано монет: 98 Монеты: 148 - 3
Сама функция
sellEggsпока глобальная. Сделайте её локальной, какcountEggs. Программа выводит то же самое.