为什么我可以将运算符直接传递给函数,但必须在其他地方用括号将它们括起来?
Why can I pass operators directly to functions, but have to surround them with parentheses everywhere else?
我们知道,您可以像这样将运算符直接传递给函数:
func foo(_ x: (Int, Int) -> Int) {
}
foo(+)
但是,当我尝试在赋值上下文中执行此操作时,它不起作用:
let f: (Int, Int) -> Int = +
它给出了两个错误:
Expected initial value after '='
Unary operator cannot be separated from its operand
我想出了我必须做的事情:
let f: (Int, Int) -> Int = (+)
Swift 究竟是如何解析这些情况的,允许我在一种情况下省略括号,但在另一种情况下不可以?
请注意,我不是在询问 Swift 以这种方式设计背后的基本原理。我只是问产生这种行为的语法产生规则。
在 Swift.org 上,我发现 this production rule 说运算符是函数调用参数:
function-call-argument → operator | identifier ':' operator
但是,我无法找到 (+)
的生产规则。我从“带括号的表达式”开始,它是围绕“表达式”的一对括号。但是“运算符”显然不是一种“表达式”。
“+”是一个中缀运算符,因此我猜它必须在某些已定义类型(例如 Int 和 Int)“之间”,并且您不能单独声明它。您可以定义自己的中缀运算符,让我们用这段代码说:
infix operator ~
func ~(lhs: Double, rhs: Double) -> Double {
return lhs * lhs + rhs * rhs
}
let val1: Double = 2
let val2: Double = 3
let squareSum = val1 ~ val2
print(squareSum)
所以我猜你的答案的关键是“+”不仅仅是一些常规函数,而是一个中缀运算符并用括号 (x) 定义它有助于编译器理解它并不介于两者之间。希望这个答案可以帮助您找到一些指南,如果您愿意,可以在哪里寻找进一步的解释。
我找到了 Swift AST Explorer, which allows me to inspect the AST of Swift code. From its GitHib page, it seems to use the same library that the Swift compiler uses to parse Swift code (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),
]),
有趣的是,!
和 -
等一元运算符也被解析为“二元运算符”。
我们知道,您可以像这样将运算符直接传递给函数:
func foo(_ x: (Int, Int) -> Int) {
}
foo(+)
但是,当我尝试在赋值上下文中执行此操作时,它不起作用:
let f: (Int, Int) -> Int = +
它给出了两个错误:
Expected initial value after '='
Unary operator cannot be separated from its operand
我想出了我必须做的事情:
let f: (Int, Int) -> Int = (+)
Swift 究竟是如何解析这些情况的,允许我在一种情况下省略括号,但在另一种情况下不可以?
请注意,我不是在询问 Swift 以这种方式设计背后的基本原理。我只是问产生这种行为的语法产生规则。
在 Swift.org 上,我发现 this production rule 说运算符是函数调用参数:
function-call-argument → operator | identifier ':' operator
但是,我无法找到 (+)
的生产规则。我从“带括号的表达式”开始,它是围绕“表达式”的一对括号。但是“运算符”显然不是一种“表达式”。
“+”是一个中缀运算符,因此我猜它必须在某些已定义类型(例如 Int 和 Int)“之间”,并且您不能单独声明它。您可以定义自己的中缀运算符,让我们用这段代码说:
infix operator ~
func ~(lhs: Double, rhs: Double) -> Double {
return lhs * lhs + rhs * rhs
}
let val1: Double = 2
let val2: Double = 3
let squareSum = val1 ~ val2
print(squareSum)
所以我猜你的答案的关键是“+”不仅仅是一些常规函数,而是一个中缀运算符并用括号 (x) 定义它有助于编译器理解它并不介于两者之间。希望这个答案可以帮助您找到一些指南,如果您愿意,可以在哪里寻找进一步的解释。
我找到了 Swift AST Explorer, which allows me to inspect the AST of Swift code. From its GitHib page, it seems to use the same library that the Swift compiler uses to parse Swift code (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),
]),
有趣的是,!
和 -
等一元运算符也被解析为“二元运算符”。