Ошибки и pcall
До сих пор ошибки приходили от самого Lua: вызов nil, сложение с nil… Ваши функции тоже могут сообщать об ошибках. Если функция получила значения, с которыми не может работать, лучше остановиться с понятным сообщением, чем продолжать и получить неверный результат. Для этого нужен error:
local coins = 20
local function buySeeds(price, bags)
local cost = price * bags
if cost > coins then
error("Не хватает монет")
end
coins = coins - cost
return coins
end
print(buySeeds(2, 5))
print(buySeeds(5, 4))
print("Готово")Программа выводит:
10
main.lua:6: Не хватает монет
stack traceback:
[C]: in function 'error'
main.lua:6: in local 'buySeeds'
main.lua:13: in main chunkerror останавливает программу, как ошибка самого Lua: Готово не выводится. Lua ставит перед вашим сообщением main.lua:6: — строку, в которой был вызван error. Трассировка показывает, как программа туда попала; читайте её снизу: основная программа вызвала buySeeds в строке 13, а в строке 6 buySeeds вызвала error. ([C] означает функцию, встроенную в Lua и написанную на языке C.) Если позиция в сообщении не нужна, передайте 0 вторым аргументом: error("Не хватает монет", 0).
«Если это не так, остановись с ошибкой» — такая частая проверка, что для неё есть сокращение: assert(condition, message). Если условие истинно, assert ничего не делает; если оно false или nil, программа завершается с сообщением:
local function plantSeeds(crop, plots)
assert(plots > 0, "Посадите хотя бы одну грядку")
print("Посадка: " .. crop .. ", грядок: " .. plots)
end
plantSeeds("морковь", 3)
plantSeeds("пшеница", 0)Программа выводит:
Посадка: морковь, грядок: 3
main.lua:2: Посадите хотя бы одну грядкуИногда ошибка не должна завершать всю программу. Эмма не может позволить себе семена тыквы, но всё равно хочет купить остальные. pcall (защищённый вызов) вызывает функцию и перехватывает её ошибку. Передайте ей функцию, а затем её аргументы: pcall(buySeeds, 5, 4) вызывает buySeeds(5, 4). pcall возвращает несколько значений: если всё прошло хорошо — true и то, что вернула функция; если произошла ошибка — false и сообщение об ошибке. В любом случае программа продолжает работать:
local coins = 20
local function buySeeds(price, bags)
local cost = price * bags
if cost > coins then
error("Не хватает монет")
end
coins = coins - cost
return coins
end
print(pcall(buySeeds, 2, 5))
local ok, result = pcall(buySeeds, 5, 4)
if ok then
print("Монет осталось: " .. result)
else
print("Ошибка: " .. result)
end
print("Готово")Программа выводит:
true 10
Ошибка: main.lua:6: Не хватает монет
ГотовоОбычно используют второй способ: сохраняют значения в ok и result и по ok решают, что такое result. Типичная ошибка — написать pcall(buySeeds(5, 4)). Со скобками после buySeeds Lua вызывает функцию сразу, до pcall, поэтому её ошибка не перехватывается. Как и с любой функцией, которую передают другой, передавайте саму функцию и её аргументы отдельно.
Задание
- 1
Монеты Эммы уходят в минус. В
buySeedsперед оплатой завершайте программу вызовомerror("Не хватает монет: " .. crop, 0), еслиcostбольшеcoins. Но тогда вторая покупка остановила бы всю программу, поэтому вызовите все три покупки черезpcallи выведите то, что он возвращает, напримерprint(pcall(buySeeds, "морковь", 2, 5)). Программа выводит:true 10 false Не хватает монет: тыква true 6 - 2
Эмма не может купить ноль пакетов. В начале
buySeedsдобавьтеassertс условиемbags > 0и сообщением"Купите хотя бы один пакет". Затем в конце программы выведитеpcall(buySeeds, "кукуруза", 3, 0). Новая строка — этоfalseи сообщение с позицией, например:false main.lua:5: Купите хотя бы один пакет - 3
Напишите функцию
tryToBuyс параметрамиcrop,priceиbags. Она вызываетbuySeedsчерезpcallи сохраняет результаты вokиresult. Если покупка удалась, она выводитКуплено: …, монет осталось: …; иначе она выводитПокупка не удалась (…):и сообщение об ошибке. В конце программы вызовитеtryToBuy("клубника", 4, 1)иtryToBuy("картофель", 3, 2). Программа заканчивается так:Куплено: клубника, монет осталось: 2 Покупка не удалась (картофель): Не хватает монет: картофель