为什么Lua表达式中没有匿名函数?

有人能解释一下为什么Lua中的匿名函数构造不是一个完整的表达式吗?对我来说,这似乎是一种怪异的情况:它(稍微)违反了函数应该成为一等公民对象的想法,并且(虽然不经常但有时)是一个不方便的问题,在大多数情况下,这是一个真正经过深思熟虑且优雅的语言。

例如,使用命令行Lua,可以绕过此问题

Lua 5.3.3 版权所有(C) 1994-2016 Lua.org, PUC-Rio
> function(x) return x*x end (2)
stdin:1: <name> expected near '('
> square = function(x) return x*x end
> square(2)
4
点赞
用户2444353
用户2444353

我明白了...需要使用圆括号,抱歉。

(function(x) return x*x end) (2)

我仍然不明白为什么要设计成那样。

2020-08-02 12:26:23
用户3574628
用户3574628

简短回答

调用一个函数,函数表达式必须是一个名称、一个索引值、另一个函数调用,或者在括号内的表达式。

长回答

我不知道为什么它被设计成这样,但我查了语法,看看它是如何工作的。这里是一个函数调用的部分:

functioncall ::= prefixexp args | prefixexp ‘:’ Name args

"args" 就是括号中的参数列表。相关的部分是 "prefixexp"。

prefixexp ::= var | functioncall | ‘(’ exp ‘)’

Okay,所以我们可以调用另一个 "functioncall","exp" 就是普通的表达式:

exp ::= nil | false | true | Numeral | LiteralString | ‘...’ | functiondef | prefixexp | tableconstructor | exp binop exp | unop exp

所以我们可以调用任何表达式,只要它在括号内。"functiondef" 涵盖匿名函数:

functiondef ::= function funcbody

funcbody ::= ‘(’ [parlist] ‘)’ block end

所以一个匿名函数是一个 "exp",但不是一个 "prefixexp",所以我们需要在它周围加上括号。

什么是 "var"?

var ::= Name | prefixexp ‘[’ exp ‘]’ | prefixexp ‘.’ Name

"var" 是一个名称或一个索引值(通常是一个表)。注意,索引值必须是一个 "prefixexp",这意味着字符串文字或表构造必须在索引之前用括号括起来。

总而言之:被调用的函数必须是一个名称、一个索引值、一个函数调用,或者在括号内的其他表达式。

最大的问题是:为什么 "prefixexp" 与 "exp" 处理不同?我不知道。我怀疑它与将函数调用和索引保持在常规的运算符优先级之外有关,但我不知道这是为什么必要的。

2020-08-02 14:10:05
用户734069
用户734069

Lua的函数调用语法内置了一些语法糖。你可以用三种方法调用函数:

  • 带括号的值列表。
  • 表构造函数(函数将作为一个单一的参数取表)。
  • 字符串字面量。

Lua希望它的语法规则相对规律。因此,如果有一种你可以用这些方法之一调用的东西,那么在任何这些方法中调用它都应该是有意义的。

考虑以下代码:

local value = function(args)
   --does some stuff
   end "I'm a literal" .. foo

如果我们允许任意的、非括号表达式像其他函数调用一样被调用,那么这意味着创建一个函数,使用字符串字面量调用它,并将该函数调用的结果与foo连接起来,然后将其存储在value中。

但是……我们真的想要这个工作吗?也就是说,我们希望人们能够写出这种代码并使其成为有效的Lua代码吗?

如果这样的代码被认为是不美观或混乱的,那么就有几种选择。

  1. Lua可以没有带有字符串字面量的函数调用。毕竟,你只需要节省2个括号。甚至也可以不允许表构造函数,虽然它们不太不美观也不太混乱。让所有人都用括号来进行所有函数调用。
  2. Lua可以使只有在使用lambda的情况下才防止使用字符串字面量进行函数调用。这将需要实质性地改变语法规则。
  3. Lua可以强制您括起任何在调用函数不是前面文本的显然意图的结构中。

现在,有人可能会争论说table_name[var_name] "literal"已经相当混乱了。但再次防止特定的行为将需要改变语法规则。你必须添加所有这些特殊情况,例如name "literal"是函数调用,但name.name "literal"不是。因此,选项2就被排除了。

使用字符串字面量调用函数的能力不只是限于Lua。JavaScript可以做到,不过你必须使用特定的文字语法才能做到。此外,能够输入require "module_name"感觉像是一个好主意。由于这样的调用被认为是一种重要的语法糖,被几种语言支持,所以选项#1被排除了。

因此您唯一的选择是选项3:使人们用括号括起他们想要调用的表达式。

2020-08-02 17:13:38