串接的結合性

lua-users home
wiki

Lua 中唯一右結合的運算符是 .. (串接) 和 ^ (指數運算)。^ 是右結合對語言來說並不罕見,但 .. 是右結合的則有點不同。在大部分情況下,.. 是以右或左結合的運算結果並沒有關係,因為字串的串接是結合性的,但如果要串接的物件有元方法或在串接大量物件時(例如 199 或更多個 - 精確的限制在 luaconf.h 中由 LUAI_MAXCCALLS 決定),然後它的效果就會明顯。.. 是右結合而不是 Lua 運算子中常見的左結合,原因主要跟實作的效率有關(請參閱 LuaList:2002-08/msg00218.htmlLuaList: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

物件會被推送到堆疊中並在「[即時]」運行。

串接作為一種運算是一種結合性的運算,因此其優先順序幾乎不相關;而且,Lua 會最佳化鏈式關聯成單一(虛擬)運算。串接的優先順序結合性僅在您為某些物件實作 __concat 元方法時才會變為可見。如果您實作的 .. 真的是一種串接運算,那它也會是結合性的,但鏈式串接會被分解成 __concat 元方法的成對呼叫,因此您的實作將從右到左呼叫。串接非結合且自然右結合的一個案例是 Posix 正則運算串接;最長相符規則假設相符組元從右到左串接。我不知道這是不是答案:實際的原因可能是它在堆疊機器中實作起來更有效率。--RiciLake

--DavidManura

另請參閱


RecentChanges · preferences
編輯 · 歷史
最近編輯 2008年2月2日 上午7:21 GMT (比較)