首页 > 解决方案 > 为什么我可以将运算符直接传递给函数,但在其他任何地方都必须用括号括起来?

问题描述

众所周知,您可以将运算符直接传递给如下函数:

func foo(_ x: (Int, Int) -> Int) {
    
}

foo(+)

但是,当我尝试在分配上下文中执行此操作时,它不起作用:

let f: (Int, Int) -> Int = +

它给出了两个错误:

'=' 后的预期初始值

一元运算符不能与其操作数分开

我发现我必须这样做:

let f: (Int, Int) -> Int = (+)

Swift 究竟是如何解析每一种情况的,允许我在一种情况下省略括号,而在另一种情况下不能?


请注意,我不是在询问以这种方式设计 Swift 背后的基本原理。我只是在询问产生这种行为的语法产生规则。

在 Swift.org 上,我找到了这条生产规则,它说运算符是函数调用参数:

function-call-argument → operator | identifier ':' operator

但是,我无法找到(+). 我从“带括号的表达式”开始,这是一对围绕“表达式”的括号。但“算子”显然不是一种“表达”。

标签: swiftlanguage-lawyer

解决方案


我找到了Swift AST Explorer,它允许我检查 Swift 代码的 AST。从它的GitHib 页面来看,它似乎使用了 Swift 编译器用来解析 Swift 代码 ( lib/Syntax ) 的同一个库。

使用 Swift AST Explorer,这(+)部分let x: (Int, Int) -> Int = (+)解析为:

TupleExpr
  (
  TupleExprElementList
    TupleExprElement
      IdentifierExpr
        +
  )

显然,(+)这是一个 1 元组!+并被归类为“标识符表达式” 。这似乎也解释了为什么元组元素中的运算符不需要括号:

let f: ((Int, Int) -> Int, (Bool) -> Bool, (Int) -> Int) = (+, !, -)

经过一番搜索,我找到了 ExprNodes.py,它似乎是生成 lib/Syntax 中的一些 API 的文件之一。在那里,我看到那SpacedBinaryOperatorToken是 的孩子IdentifierExpr,所以这可能+就是被解析的内容。

Node('IdentifierExpr', kind='Expr',
     children=[
         Child('Identifier', kind='Token',
               token_choices=[
                   'IdentifierToken',
                   'SelfToken',
                   'CapitalSelfToken',
                   'DollarIdentifierToken',
                   'SpacedBinaryOperatorToken',
               ]),
         Child('DeclNameArguments', kind='DeclNameArguments',
               is_optional=True),
     ]),

有趣的是,一元运算符(例如!and -)也被解析为“二元运算符”。


推荐阅读