Замыкания
Функция может создать другую функцию и вернуть её, как любое другое значение. Новая функция запоминает локальные переменные создавшей её функции, даже когда та уже закончилась. Вот функция, которая делает счётчики:
local function makeCounter()
local count = 0
return function()
count = count + 1
return count
end
end
local countEggs = makeCounter()
print(countEggs())
print(countEggs())
print(countEggs())Программа выводит:
1
2
3По шагам: makeCounter() создаёт локальную переменную count со значением 0, затем создаёт анонимную функцию, возвращает её и заканчивается. Возвращённая функция сохраняется в countEggs. Обычно локальные переменные функции исчезают, когда она заканчивается, но count не исчезает: возвращённая функция её использует, поэтому Lua хранит её, пока существует эта функция. Каждый вызов countEggs() увеличивает этот count и возвращает его.
Функция вместе с переменными, которые она запоминает, называется замыканием. Каждый вызов makeCounter создаёт новый count, поэтому у каждого счётчика свой:
local function makeCounter()
local count = 0
return function()
count = count + 1
return count
end
end
local eggs = makeCounter()
local milk = makeCounter()
eggs()
eggs()
milk()
print("Яйца: " .. eggs())
print("Молоко: " .. milk())Программа выводит:
Яйца: 3
Молоко: 2eggs() в отдельной строке вызывает счётчик и отбрасывает возвращённое число; так можно. Почему бы просто не использовать одну переменную снаружи функции? Тогда любая часть программы могла бы её изменить, а для двух счётчиков понадобились бы две переменные и две функции. В замыкании count спрятана: добраться до неё может только сам счётчик.
Параметры внешней функции — тоже её локальные переменные, поэтому замыкание запоминает и их:
local function makeSeller(price)
return function(count)
return count * price
end
end
local sellCarrots = makeSeller(5)
local sellPumpkins = makeSeller(20)
print(sellCarrots(10), sellPumpkins(3), sellCarrots(2))Программа выводит:
50 60 10sellCarrots помнит цену 5, а sellPumpkins — 20. У каждой возвращённой функции своя price, заданная один раз при её создании.
Задание
- 1
Напишите функцию
makeCounterс параметромstep. Она возвращает новую функцию, которая при каждом вызове прибавляетstepк своему счёту (счёт начинается с0) и возвращает новый счёт. Под ней добавьте такой код:local eggs = makeCounter(6) local milk = makeCounter(10) eggs() eggs() milk() print("Яйца: " .. eggs()) print("Молоко: " .. milk())Эмма собирает яйца по 6 штук, а молоко — по 10 литров, поэтому программа выводит:
Яйца: 18 Молоко: 20 - 2
Напишите функцию
makeBudgetс параметромcoins. Она возвращает функцию с параметромprice: еслиpriceне большеcoins, функция вычитает его изcoins, и в любом случае возвращает, сколько монет осталось. У Эммы и дедушки Джо будет по своему бюджету. Добавьте в конец такой код:local emma = makeBudget(20) local joe = makeBudget(15) print(emma(8)) print(joe(4)) print(emma(5)) print(emma(10))Последняя покупка стоит больше, чем осталось у Эммы, поэтому она не происходит. Программа выводит:
12 11 7 7