; — это код, который делает приблизительно ничего. Не абсолютно: об этом статья.
Когда я писал crateriform (видяшки в гайде про корутины этим набором прог зарендерены), я отталкивался от Lua-парсера на Lua от @LeshaInc (спасибо ему ещё раз): Lua-часть принимала исходный файл с кодом, парсила его на AST и генерировала по нему обратно код. С костылями.
Для рендера нужен был "сценарий" — файл, в котором указаны, какие символы изменились и на какое значение, помимо прочего. Генерируемые костыли как раз этим занимались: они оборачивались вокруг выражений и писали в файл высчитанное значение.
Чтобы показать кадр с "n = 10", как на гифке, и следующий за ним, сценарий такой:
5,16 5,16 expr 10
Здесь через пробел: позиция начала в исходном коде, позиция конца, опкод (expr) и значение выражения.
Так как генерируемый код делает то же, что и исходный, но ещё рисует сценарий, а выражения могут быть засунуты где угодно и раскрываться сразу в несколько значений, я создаю лямбду (анонимную функцию) и тут же её вызываю. Вот как выглядит часть сгенерированного кода, который занимается строкой выше:
highlight({5, 16}, {5, 16}, (function(...) local values = table.pack(...) return function() return table.unpack(values, 1, values.n) end end)((n)), 'expr')
Выглядит очень сложно (для сложности есть причины), но сейчас интересует только это:
(function(...) <...> end)(...)
Эта конструкция присутствует как третий аргумент к highlight. Программисты на JS с этим шаблоном должны быть знакомы: создаётся лямбда и тут же вызывается с некими аргументами. В месте, где синтаксис позволяет указывать только выражение — в списке аргументов вызова в нашем случае, — таким образом размещаем стейтменты.
На всякий случай скажу, что стейтмент — любая цельная конструкция, кроме выражений. Если вы берёте часть кода, которая отдельно, вырванная из кода, не выдаёт синтаксическую ошибку (например, цикл for или local x = 3), и её нельзя поместить после x = , это стейтмент.
А теперь суть.
(function(x) print(x^2) end)(2) (function(y) print(y + 2) end)(2)
Этот код задумывался так, чтобы он два раза принтнул четвёрку. Запускаем:
$ lua5.3 semicolon.lua 4.0 lua5.3: semicolon.lua:1: attempt to call a nil value stack traceback: semicolon.lua:1: in main chunk [C]: in ?
..?
Lua игнорирует пробельные символы. Код вроде такого:
func(3) (4)(5)
...эквивалентен такому:
func(3)(4)(5)
В примере с ошибкой вызывается первая лямбда, возвращающая nil, который мы затем пытаемся вызвать с аргументом — второй лямбдой. Поэтому получаем "attempt to call a nil value". Чиним с помощью ;:
(function(x) print(x^2) --> 4.0 end)(2) ; (function(y) print(y + 2) --> 4 end)(2)
Кстати, чтобы ещё с толку сбить: комментарий вместо ; ошибку не исправит.
Ещё один ошибочный пример:
local pi = math.pi (function(r) print(2 * pi * r) end)(3)
Как чинить, вы уже знаете.
Отдельно упомяну точку с запятой после return. return обязан быть последним стейтментом в блоке. А ; — это стейтмент. Почему тогда можно делать так?
local function f(x) return x^2; end print(f(2)) --> 4.0
Ответ: потому что ; — это опциональная часть return, а не отдельный стейтмент. Если же залипает клавиша и получается вот так:
local function f(x) return x^2;; end print(f(2))
...то будет синтаксическая ошибка. Вторая точка с запятой — теперь отдельный стейтмент, которых после return быть не должно.
Поэтому с уважением относитесь к этому стейтменту. Точка с запятой делает приблизительно ничего, но с умом.
- 3
- 1
- 2
0 комментариев
Рекомендуемые комментарии
Нет комментариев для отображения