没有花括号和没有参数标签的匿名函数?

时间:2018-08-30 09:02:39

标签: swift lambda anonymous-function shorthand

我在another question上看到了一些代码,这些代码似乎使用一些不寻常的语法创建了一个匿名函数(闭包表达式):

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

我了解左侧,它声明了类型为(Int, Int) -> Int的常量(该函数接受两个Integer并返回一个Integer)。但是(+)是什么?如何声明没有花括号的函数,并且在没有任何类型的参数标签时如何引用两个参数?

该函数接受两个参数,将它们加在一起,然后返回结果。如果将+运算符替换为另一个运算符(例如*),则操作会更改。因此,{$0 + $1}的某种速记形式吗?如果是这样,此速记背后的逻辑是什么?

3 个答案:

答案 0 :(得分:2)

实际上,这不是速记。

plus是类型(Int, Int) -> Int的变量。您可以为其分配任何该类型(或其任何子类型)的对象。文字lambda闭包肯定是这种类型的,但是实际上,命名函数或方法也可以。而这正是这里发生的事情。

它正在将名为+的运算符方法对象分配给该变量。

Closures chapter of the language guide中隐式提到了这一点:

  

操作员方法

     

实际上,甚至有一种更短的方式来编写上述闭合表达式。 Swift的String类型将大于操作符(>)的特定于字符串的实现定义为一种方法,该方法具有两个类型为String的参数,并返回类型为{{1 }}。这与Bool方法所需的方法类型完全匹配。因此,您只需传递大于号运算符,Swift就会推断您要使用其特定于字符串的实现:

sorted(by:)

因此,代码正在执行的操作是将 Operator方法reversedNames = names.sorted(by: >) 分配给变量+plus只是分配给变量的函数的名称。没有魔术速记。

看到这个消息你会感到惊讶吗?

+

答案 1 :(得分:2)

+infix运算符,在Swift中是函数名。在许多类型上定义了很多这样的函数(它已重载)。

您可以为自己的自定义类型定义+。例如:

struct Foo {
    var value: Int

    static func +(_ lhs: Foo, _ rhs: Foo) -> Foo {
        return Foo(value: lhs.value + rhs.value)
    }
}

var f1 = Foo(value: 5)
var f2 = Foo(value: 3)


let f3 = f1 + f2
print(f3.value) // 8

这有效:

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

因为+函数的签名已被完全指定,所以Swift能够识别正确的+函数。

如果我们想将新的+函数分配给plus

let plus: (Foo, Foo) -> Foo = (+)

这实际上没有什么不同:

func add(_ a: Int, _ b: Double) -> Double {
    return Double(a) + b
}

let plus: (Int, Double) -> Double = add

print(plus(3, 4.2))  // 7.2

  

那为什么要加上括号呢?为什么要指定(+)而不仅仅是+

+也是Swift中的一元运算符。

例如,您可以说:

let x = +5

所以只是想说:

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

会使编译器感到困惑,因为它将+视为一元前缀运算符,并且期望+后跟5之类的其他内容。通过用括号将其括起来,Swift编译器将停止尝试将+解析为一元运算符,并将其视为其函数名。即使+不是一元前缀运算符,Swift仍将期望+两侧的值,因此括号告诉Swift您没有为函数提供任何输入,但是只想要函数本身。

在没有歧义的情况下,可以引用+函数而不带括号。例如:

var arr = [1, 2, 3]
let sum = arr.reduce(0, +)

答案 2 :(得分:2)

(+)本身是一种运算符方法。您可以这样声明自己的运算符:

precedencegroup CompositionPrecedence {
    associativity: left
    higherThan: AssignmentPrecedence
}

infix operator •: CompositionPrecedence

func •(a: Int, b: Int) -> Int {
    return a + b
}

用法将是相同的:

var closure: (Int, Int) -> Int = (•)
print("\(closure(1, 2))")