Операторы Lua, почему не определено +=, -= и так далее?
это вопрос, который я был слегка раздражен в течение некоторого времени и просто никогда не удосужился найти ответ.
однако я подумал, что могу, по крайней мере, задать вопрос и, возможно, кто-то может объяснить.
в основном многие языки, в которых я работал, используют синтаксический сахар для записи (используя синтаксис из C++):
int main() {
int a = 2;
a += 3; // a=a+3
}
в то время как в lua +=
не определено, поэтому мне пришлось бы написать a=a+3
, который снова все о синтаксическом сахаре. когда использование более "значимого" имени переменной, например:bleed_damage_over_time
или что - то начинает надоедать писать:
bleed_damage_over_time = bleed_damage_over_time + added_bleed_damage_over_time
вместо:
bleed_damage_over_time += added_bleed_damage_over_time
поэтому я хотел бы не знать, как это решить, если у вас нет хорошего решения, в этом случае мне, конечно, было бы интересно услышать это; но почему lua не реализует этот синтаксический сахар.
3 ответов
это только догадки с моей стороны, но:
1. Трудно реализовать это в компиляторе с одним проходом
компилятор байт-кода Lua реализован как однопроходный рекурсивный парсер спуска, который немедленно генерирует код. Он не анализирует отдельную структуру AST, а затем во втором проходе преобразует ее в байт-код.
это накладывает некоторые ограничения на грамматику и семантику. В частности, все, что требует произвольные lookahead или прямые ссылки действительно трудно поддерживать в этой модели. Это означает, что задания уже трудно разобрать. Дано что-то вроде:
foo.bar.baz = "value"
когда вы анализируете foo.bar.baz
, вы не понимаете, что на самом деле разбираете задание, пока не нажмете =
после того, как вы уже проанализировали и сгенерировали код для этого. Компилятор Lua имеет хорошую сложность только для обработки назначений из-за этого.
поддержка самостоятельного назначения сделать это еще труднее. Что-то вроде:
foo.bar.baz += "value"
необходимо перевести на:
foo.bar.baz = foo.bar.baz + "value"
но в тот момент, когда компилятор попадает в =
, он уже забыл о foo.bar.baz
. Это возможно, но нелегко.
2. Он не может играть хорошо с грамматикой
Lua фактически не имеет никаких операторов или разделителей строк в грамматике. Пробелы игнорируются, а обязательные точки с запятой отсутствуют. Вы можете do:
io.write("one")
io.write("two")
или:
io.write("one") io.write("two")
и Луа одинаково доволен обоими. Держать такую грамматику недвусмысленной сложно. Я не уверен, но операторы самостоятельного назначения мая сделать это сложнее.
3. Он не играет хорошо с несколькими назначениями
Lua поддерживает несколько назначений, например:
a, b, c = someFnThatReturnsThreeValues()
мне даже не ясно, что это будет означать, если вы попытаетесь do:
a, b, c += someFnThatReturnsThreeValues()
вы можете ограничить операторы самостоятельного назначения одним назначением, но тогда вы просто добавили странный угловой случай, о котором люди должны знать.
со всем этим совсем не ясно, что операторы самоназначения достаточно полезны, чтобы иметь дело с вышеуказанными проблемами.
Я думаю, вы могли бы просто переписать этот вопрос как
почему
<languageX>
есть<featureY>
С<languageZ>
?
обычно это компромисс, который языковые дизайнеры делают на основе своего видения того, для чего предназначен язык, и их целей.
в случае Lua Язык предназначен для встроенного языка сценариев, поэтому любые изменения, которые делают язык более сложным или потенциально делают компилятор / среду выполнения даже чуть больше или медленнее может идти против этой цели.
Если вы реализуете каждую крошечную функцию, вы можете получить язык "кухонная раковина":ада кому-нибудь?
и, как вы говорите, это просто синтаксический сахар.
еще одна причина, по которой Lua не имеет операторов самостоятельного назначения, заключается в том, что доступ к таблице может быть перегружен метатаблями с произвольными побочными эффектами. Для самостоятельного назначения вам нужно будет выбрать desugar
foo.bar.baz += 2
на
foo.bar.baz = foo.bar.baz + 2
или
local tmp = foo.bar
tmp.baz = tmp.baz + 2
первая версия работает __index
метаметод для foo
дважды, в то время как второй делает это только один раз. Не включая самостоятельное назначение на языке и принуждение вас быть явными помогает избежать этой двусмысленности.