Lua运算符,为啥不定义+=、-=等?

Posted

技术标签:

【中文标题】Lua运算符,为啥不定义+=、-=等?【英文标题】:Lua operators, why isn't +=, -= and so on defined?Lua运算符,为什么不定义+=、-=等? 【发布时间】:2013-12-04 04:48:01 【问题描述】:

这是一个我已经有一段时间有点恼火的问题,只是一直没有时间去寻找答案。

但我想我至少可以提出这个问题,也许有人可以解释一下。

基本上我使用过的许多语言都使用语法糖来编写(使用来自 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 方补丁实现了这一点:lua-users.org/wiki/LuaPowerPatches(向下滚动到“复合赋值运算符 (5.2)”) 为什么 C 不支持类?为什么我不能在 C 中使用int a = b = c = 0;?为什么 C++ 中没有像 int a, float b, char *c = 1, 2.22, "3rd string"; 这样的分配?无意冒犯,但每种语言都有其自身的局限性和优势。 @hjpotter92 完全没有冒犯,你回答了我提出的问题,证明这正是我想要的。 -> +1 顺便说一句,如果您正在寻找一些不错的 lua 语法扩展,请查看 moonscript (github.com/leafo/moonscript),它是一个元编程库,可通过以下方式扩展 Lua 语法这样的事情,它在幕后生成 Lua。 顺便说一句,像这样的长变量名表明您没有使用适当的数据结构和/或没有将函数范围限定为合理的大小。变量名称应该是描述性的在它们的上下文中 【参考方案1】:

这只是我的猜测,但是:

1.在单遍编译器中很难实现这一点

Lua 的字节码编译器实现为单遍递归下降解析器,可立即生成代码。它不会解析为单独的 AST 结构,然后在第二次传递中将其转换为字节码。

这对语法和语义施加了一些限制。特别是,任何需要任意前瞻或前向引用的东西都很难在这个模型中得到支持。这意味着分配已经很难解析。给定类似的东西:

foo.bar.baz = "value"

当您解析foo.bar.baz 时,您并没有意识到您实际上是在解析一个赋值,直到您已经解析并生成了代码之后点击了=。正因为如此,Lua 的编译器在处理赋值方面有相当多的复杂性。

支持自我分配会使这变得更加困难。比如:

foo.bar.baz += "value"

需要翻译成:

foo.bar.baz = foo.bar.baz + "value"

但是当编译器遇到= 时,它已经忘记了foo.bar.baz。这是可能的,但并不容易。

2。它可能不适合语法

Lua 在语法中实际上没有任何语句或行分隔符。空格被忽略,并且没有强制分号。你可以这样做:

io.write("one")
io.write("two")

或者:

io.write("one") io.write("two")

Lua 对两者都同样满意。保持这样一个明确的语法是很棘手的。我不确定,但自赋值运算符可能会使这变得更难。

3.多重分配效果不好

Lua 支持多重赋值,比如:

a, b, c = someFnThatReturnsThreeValues()

我什至不清楚如果你尝试这样做意味着什么:

a, b, c += someFnThatReturnsThreeValues()

您可以将自赋值运算符限制为单个赋值,但您只是添加了一个人们必须知道的奇怪的极端情况。

综上所述,自赋值运算符是否有用到值得处理上述问题还不是很清楚。

【讨论】:

非常感谢您花时间回答。您确实为我的问题指出了一些非常合理且可能的论点。我只是接受它只是一个糟糕的问题。但是在阅读了您的答案后,我觉得我更了解并给出了所有论点,甚至同意 Lua 不尝试提供这些运算符。 不客气!这些东西让我记忆犹新,因为我目前正在实现一种语言,它也有一个单通道编译器,它迫使我意识到它是如何影响它的设计的。 有人提到递归下降编译器和解析赋值语句的问题?我很清楚,但问题主要在于解析 LHS。如果您可以可靠地找到赋值运算符并理解 LHS,那么操作树以生成 '+=' 或等价物并不难。在没有树的情况下做这件事,现在更难了。 1. 但是已经有一个这种类型的运算符,:。它用于调用函数,同时自动将您使用它的表作为第一个参数传递,就像+= 和朋友一样。 2. 这没有任何意义。为什么行分离与它有关?它与普通赋值运算符= 具有相同的语法。 3. 这听起来很清楚。你想增加所有三个。 不幸的是,源文件没有很好的记录。 “原型”是 Lua 用来指代一个函数在被封装到闭包之前的编译内部表示。这是一个没有上值的函数。【参考方案2】:

我想你可以把这个问题改写成

为什么<languageX> 没有来自<languageZ><featureY>

通常,这是语言设计者根据他们对语言用途的愿景和目标做出的权衡。

在 Lua 的情况下,该语言旨在成为一种嵌入式脚本语言,因此任何使该语言更复杂或可能使编译器/运行时稍微更大或更慢的更改都可能与此目标背道而驰。

如果你实现了每一个微小的功能,你最终会得到一种“厨房水槽”语言:ADA,有人吗?

正如你所说,它只是语法糖。

【讨论】:

感谢您的回答,它确实提供了我想要的信息。这确实有道理,我喜欢你如何展示问题的一般形式,从而将其放在透视图中。【参考方案3】:

Lua 没有自赋值运算符的另一个原因是,表访问可能被元表重载,从而产生任意副作用。对于自我分配,您需要选择脱糖

foo.bar.baz += 2

进入

foo.bar.baz = foo.bar.baz + 2

或进入

local tmp = foo.bar
tmp.baz = tmp.baz + 2

第一个版本为foo 运行__index 元方法两次,而第二个版本只运行一次。不包括语言中的自我分配和强迫你明确有助于避免这种歧义。

【讨论】:

第二个选项会更明智,比如do local t=foo.bar t.baz=t.baz+2 end : 是 Lua 中一个非常相似的运算符,但不是那样工作的。它只调用了一次__index,值被使用了两次。

以上是关于Lua运算符,为啥不定义+=、-=等?的主要内容,如果未能解决你的问题,请参考以下文章

lua 二进制的移位运算

如何在 Lua 函数上应用元表以启用它们之间的自定义运算符?

Lua = 运算符作为打印

lua中定义操作符行为的元方法(重载操作符)

lua 运算符

Lua 错误处理