【问题标题】:why aren't anonymous functions in Lua expressions?为什么 Lua 表达式中没有匿名函数?
【发布时间】:2024-05-04 19:05:02
【问题描述】:

谁能向我解释一下为什么 Lua 中的匿名函数构造不是一个成熟的表达式?对我来说,这似乎很奇怪:它(稍微)违背了函数应该是一等对象的想法,并且(不经常但偶尔)对这种主要是经过深思熟虑的优雅语言造成不便。

示例,使用命令行 Lua,以及解决方法

Lua 5.3.3  Copyright (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

【问题讨论】:

  • "它(稍微)违背了函数应该是一等对象的想法" 不,它不是。一等对象的性质和语法表达的性质彼此无关。
  • 实际上(我知道这将这个论点推到了极限,不要太认真)我可以想象一种具有完全一流的新博客类型的语言,但没有任何语法构建一个。如果你有一个包含 blobop 的变量,那么你可以对它做任何你喜欢的事情,包括分配它。但这没用。恕我直言,要用作一流的对象(如果不是法律条文,精神上),您需要支持语法。

标签: function lua expression


【解决方案1】:

Lua 的函数调用语法中内置了一些语法糖。您可以通过 3 件事调用函数:

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

Lua 希望在语法上保持一定的规律。因此,如果您可以通过其中一种方式将某个事物作为函数调用,那么能够以其中任何一种方式调用它应该是有意义的。

考虑以下代码:

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

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

但是......我们真的想要它工作吗?也就是说,我们是否希望人们能够编写并使其成为有效的 Lua 代码?

如果认为此类代码不美观或令人困惑,则有几种选择。

  1. Lua 不能使用字符串文字进行函数调用。毕竟,您只保存了 2 个括号。甚至可能也不允许表构造函数,尽管它们不那么难看,也不那么令人困惑。让每个人对所有函数调用都使用括号。
  2. Lua 可以做到这一点,只有在 lambdas 的情况下,才能阻止字符串文字的函数调用。这将需要对语法进行大幅去正规化。
  3. Lua 可能会强制您将调用函数不是前面文本的明显预期结果的任何构造括起来。

现在,有人可能会争辩说,table_name[var_name] "literal" 对于正在发生的事情已经相当混乱了。但同样,专门防止这种情况需要对语法进行反规范化。您必须添加所有这些特殊情况,其中name "literal" 是函数调用但name.name "literal" 不是。所以选项 2 已失效。

使用字符串字面量调用函数的能力几乎不限于 Lua。 JavaScript can do it,但您必须使用特定的文字语法来获取它。另外,能够输入require "module_name" 感觉是个好主意。由于此类调用被认为是一种重要的语法糖,并受到多种语言的支持,因此选项 #1 已被淘汰。

所以你唯一的选择是#3:让人们用括号括起他们想要调用的表达式。

【讨论】:

    【解决方案2】:

    简答

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

    长答案

    我不知道为什么它是这样设计的,但我确实查看了grammar 以了解它是如何工作的。这是函数调用的入口:

    函数调用 ::= prefixexp 参数 | prefixexp ‘:’ 名称参数

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

    前缀表达式 ::= var |函数调用 | ‘(’exp‘)’

    好的,所以我们可以调用另一个“函数调用”。 "exp" 只是一个普通的表达式:

    exp ::= nil |假 |真实 |数字 |文字字符串 | '...' |函数定义 |前缀表达式 |表构造函数 | exp binop exp |项目经验

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

    functiondef ::= function funcbody

    funcbody ::= ‘(’ [parlist] ‘)’ 块结束

    所以匿名函数是一个“exp”,而不是一个“prefixexp”,所以我们需要用括号括起来。

    什么是“变量”?

    var ::= 名称 |前缀表达式'[' exp']' | prefixexp ‘.’ 名称

    "var" 是名称或索引值(通常是表)。请注意,索引值必须是“prefixexp”,这意味着字符串文字或表构造函数必须在括号中才能索引它们。

    总结一下:被调用的函数必须是名称、索引值、函数调用或括号内的其他表达式。

    最大的问题是:为什么“prefixexp”与“exp”的处理方式不同?我不知道。我怀疑这与将函数调用和索引保持在正常的operator precedence 之外有关,但我不知道为什么这是必要的。

    【讨论】:

      【解决方案3】:

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

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

      我还是不明白为什么要这样设计。

      【讨论】:

      • Lua 在设计时并未考虑到 IIFE。您还需要 Javascript 中的括号,它们的使用频率更高。在 Lua 中,大多数 Javascript 中的 IIFE 将改为 do ... end 块。
      • @Jasmijn:IIFE 可用于重新绑定 ...。使用{...} 可能会更好。
      最近更新 更多