连接运算符的结合性 |
|
..
(连接)和 ^
(指数)。^
作为右结合运算符在语言中并不罕见,但 ..
作为右结合运算符则比较少见。在大多数情况下,..
是右结合还是左结合并不重要,因为字符串连接是结合性的,但如果被连接的对象具有元方法,或者连接了大量对象(例如 199 个或更多 - 精确的限制由 luaconf.h 中的 LUAI_MAXCCALLS
决定),那么效果就会很明显。..
采用右结合而不是 Lua 运算符通常的左结合的原因似乎主要与实现效率有关(参见 LuaList:2002-08/msg00218.html 和 LuaList:2002-08/msg00104.html)。
下面展示了同时连接超过大约 199 个元素的问题。这个 Lua 程序创建了一个无法编译的 Lua 程序
print([[return 0]] .. ([[ .. 0]]):rep(198))
lua test.lua | luac -p -l - luac: stdin:1: chunk has too many syntax levels
如果我们将 198 减少到 10,我们可以看到原因
main <stdin:0,0> (14 instructions, 56 bytes at 0x671200) 0+ params, 11 slots, 0 upvalues, 0 locals, 1 constant, 0 functions 1 [1] LOADK 0 -1 ; "" 2 [1] LOADK 1 -1 ; "" 3 [1] LOADK 2 -1 ; "" 4 [1] LOADK 3 -1 ; "" 5 [1] LOADK 4 -1 ; "" 6 [1] LOADK 5 -1 ; "" 7 [1] LOADK 6 -1 ; "" 8 [1] LOADK 7 -1 ; "" 9 [1] LOADK 8 -1 ; "" 10 [1] LOADK 9 -1 ; "" 11 [1] LOADK 10 -1 ; "" 12 [1] CONCAT 0 0 10 13 [1] RETURN 0 2 14 [1] RETURN 0 1
编译器会将所有常量放在连接之前放在堆栈上。当运算符为 ^
时也会出现相同的错误,尽管对大约 200 个对象进行指数运算的需求可能非常少见,除非 ^
被重写为不同的语义(这可能不是一个好主意)。
现在,如果使用左结合运算符(例如加法),那么这种类型的代码就可以正常编译
print([[return a]] .. ([[ + a]]):rep(198))
main <stdin:0,0> (399 instructions, 1596 bytes at 0x671200) 0+ params, 2 slots, 0 upvalues, 0 locals, 1 constant, 0 functions 1 [1] GETGLOBAL 0 -1 ; a 2 [1] GETGLOBAL 1 -1 ; a 3 [1] ADD 0 0 1 4 [1] GETGLOBAL 1 -1 ; a 5 [1] ADD 0 0 1 6 [1] GETGLOBAL 1 -1 ; a 7 [1] ADD 0 0 1 ... 390 [1] GETGLOBAL 1 -1 ; a 391 [1] ADD 0 0 1 392 [1] GETGLOBAL 1 -1 ; a 393 [1] ADD 0 0 1 394 [1] GETGLOBAL 1 -1 ; a 395 [1] ADD 0 0 1 396 [1] GETGLOBAL 1 -1 ; a 397 [1] ADD 0 0 1 398 [1] RETURN 0 2 399 [1] RETURN 0 1
这些对象被推送到堆栈上,并在 "[及时]" 时进行操作。
__concat
元方法时才会显现出来。如果你的 ..
实现确实是连接操作,那么它也将是结合性的,但链式连接会被分解为对 __concat
元方法的成对调用,因此你的实现将从右到左被调用。连接不是结合性并且自然右结合的一种情况是 Posix 正则表达式连接;最长匹配优先规则假设匹配组件是从右到左连接的。我不知道这是否是一个答案:真正的原因可能是它在堆栈机器中实现起来更有效率。--RiciLake
^
表示叉积,但结合性不同
..
来应用函数装饰器,因为 ..
具有右结合性。