【问题标题】:Why are table literals treated differently from table references in Lua?为什么表字面量与 Lua 中的表引用不同?
【发布时间】:2013-10-20 07:24:04
【问题描述】:

这是一个 Lua 5.2.2 的成绩单,显示了一个表的声明和索引:

> mylist = {'foo', 'bar'}
> print(mylist[1])
foo

为什么下面的说法不合法?

> print({'foo', 'bar'}[1])
stdin:1: ')' expected near '['

我想不出任何其他语言不能用文字代替引用(当然,需要左值时除外)。

FWIW,给表格文字加上括号使语句合法:

> print(({'foo', 'bar'})[1])
foo

【问题讨论】:

    标签: lua lua-table


    【解决方案1】:

    这也与在 Lua 中此语法有效这一事实有关:

    myfunc { 1, 2, 3 }
    

    相当于:

    myfunc( { 1, 2, 3 } )
    

    因此一个表达式如:

    myfunc { 1, 2, 3 } [2]
    

    被解析为:

    myfunc( { 1, 2, 3 } )[2]
    

    所以首先评估函数调用,然后进行索引。

    如果{1,2,3}[2] 可以被解析为有效的索引操作,则可能会导致前一个表达式中的歧义,这需要更多的前瞻。 Lua 团队选择通过将 Lua 字节码编译器设置为单通道编译器来加快其速度,因此它只扫描一次源代码,并且具有最少的前瞻。 This message to lua mailing list from Roberto Ierusalimschy (lead Lua developer) 指向那个方向。

    字符串文字和方法调用也存在同样的问题。这是无效的:

    "my literal":sub(1)
    

    但这是有效的:

    ("my literal"):sub(1)
    

    同样,Lua 允许这样做:

    func "my literal"
    

    相当于这个:

    func( "my literal" )
    

    【讨论】:

    • +1;这实际上解释了原因。我不是 foo"bar" => foo("bar") 语法糖的朋友。
    • @dualed 好吧,如果它的唯一用途是避免使用一对括号,我会同意,但是在设计特定领域的语言时,这种“糖”的真正力量是显而易见的。例如,Widget { w = 100, h = 10} "Warning" "stack overflow in the frobnicator!" 之类的东西是常规 Lua 语法,可以很容易地被 Widget 函数使用一些闭包技巧“解析”,而无需编写解析器!这在将 Lua 用作配置语言或用于最终用户不是熟练程序员的其他应用程序或创建自定义数据描述语言时非常方便。
    • 我非常不同意,这种东西只会让用户感到困惑,特别是如果他们犯了一些错误。错误消息对他们来说永远没有意义。曾经。使用简单的 Lua 语法更安全,一些现有的 plist、xml、ini,你可以命名它,甚至创建你自己的并为它编写一个真正的解析器。
    【解决方案2】:

    继续语法定义here,非括号版本无效而括号是因为语法树采用不同的路径并期望右括号),因为应该 在该上下文中不是其他符号。

    第一种情况:

    functioncall ::=  prefixexp args | prefixexp `:´ Name args 
    
        prefixexp =>
            prefixexp ::= var | functioncall | `(´ exp `)´
    
                var -> print
        THEREFORE prefixexp -> print
    
        args =>
            args ::=  `(´ [explist] `)´ | tableconstructor | String 
                match '('
    
                explist =>
                    explist ::= {exp `,´} exp
                        exp =>
                            exp ::=  nil | false | true | Number | String | `...´ | function | prefixexp | tableconstructor | exp binop exp | unop exp
                                tableconstructor => 
                        explist-> {'foo','bar'}
    
                THEREFORE explist = {'foo','bar'}
    
                match ')' ERROR!!! found '[' expected ')'
    

    另一方面用括号:

    functioncall ::=  prefixexp args | prefixexp `:´ Name args 
    
        prefixexp =>
            prefixexp ::= var | functioncall | `(´ exp `)´
    
                var -> print
        THEREFORE prefixexp -> print
    
        args =>
            args ::=  `(´ [explist] `)´ | tableconstructor | String 
                match '('
    
                explist =>
                    explist ::= {exp `,´} exp
                        exp =>
                            exp ::=  nil | false | true | Number | String | `...´ | function | prefixexp | tableconstructor | exp binop exp | unop exp 
                                prefixexp =>
                                    prefixexp ::= var | functioncall | `(´ exp `)´
                                        var => 
                                            var ::=  Name | prefixexp `[´ exp `]´ | prefixexp `.´ Name 
                                                prefixexp =>
                                                    prefixexp ::= var | functioncall | `(´ exp `)´
                                                    match '('
                                                    exp => 
                                                        exp ::=  nil | false | true | Number | String | `...´ | function | prefixexp | tableconstructor | exp binop exp | unop exp 
                                                        tableconstructor => 
                                                    THEREFORE exp = {'foo','bar'}
                                                    match ')'
                                                THEREFORE prefixexp = ({'foo','bar'})
                                                match '['
                                                exp => Number = 1
                                                match ']'
                                        THEREFORE VAR = ({'foo','bar'})[1]
                                THEREFORE prefixexp = VAR
                        THEREFOER exp = VAR
                THEREFORE explist = VAR
                match ')'
        THEREFORE args = (VAR)
    => print(VAR)
    

    【讨论】:

    • 感谢您的解释并指出语法。知道为什么他们没有以更通常的方式创建语法,摆脱 prefixexp 并将其 RHS 移动到 exp 中吗?我的猜测是他们试图在解析时而不是在运行时捕获像 '3[2]' 这样的错误。
    • 我发现我被一种为初学者而不是经验丰富的程序员设计的语言所吸引是很合适的,因为我一直致力于创建旨在给用户带来惊喜的语言(例如 App Inventor for Android)每当有人不得不感到惊讶时,经验丰富的程序员而不是初学者。例如,与 Lua 一样,App Inventor 使用基于 1 的列表。
    • @espertus 我不确定他们为什么决定采用这种方式,您可能对它事先发现错误是正确的。我实际上之前遇到过这个exact 问题并想(在来自大量python 之后)什么?为什么认为不起作用.
    猜你喜欢
    • 2019-05-16
    • 1970-01-01
    • 2012-02-21
    • 2014-03-30
    • 1970-01-01
    • 1970-01-01
    • 2012-05-05
    • 2018-11-14
    • 2018-03-29
    相关资源
    最近更新 更多