Anonymous function with no curly braces and no argument labels?

只愿长相守 提交于 2019-12-08 02:56:24

问题


I saw some code on another question that seems to create an anonymous function (closure expression) with some unusual syntax:

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

I understand the left side—that it's declaring a constant of type (Int, Int) -> Int (a function that takes two Integers and returns an Integer). But what is (+)? How can it declare a function without curly brackets, and how does it refer to the two arguments when there are no argument labels of any kind?

The function takes two arguments, adds them together, and returns the result. If I replace the + operator with a different one (say a *), the operation changes. So is it some kind of shorthand for {$0 + $1}? If so, what is the logic behind this shorthand?


回答1:


Actually, this is no shorthand.

plus is a variable of type (Int, Int) -> Int. You can assign it any object that is of this type (or any of its subtypes). A literal lambda closure is certainly of this type, but actually a named function or method would also do. And that is exactly what is happening here.

It is assigning the operator method object named + to the variable.

This is mentioned sort-of implicitly in the Closures chapter of the language guide:

Operator Methods

There’s actually an even shorter way to write the closure expression above. Swift’s String type defines its string-specific implementation of the greater-than operator (>) as a method that has two parameters of type String, and returns a value of type Bool. This exactly matches the method type needed by the sorted(by:) method. Therefore, you can simply pass in the greater-than operator, and Swift will infer that you want to use its string-specific implementation:

reversedNames = names.sorted(by: >)

So, what the code is doing is assigning the Operator Method + to the variable plus. + is simply the name of the function assigned to the variable. No magic shorthand involved.

Would you be surprised to see this?

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



回答2:


+ is an infix operator and a function name in Swift. There are many such functions defined on many types (it is overloaded).

You can define + for your own custom type. For example:

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

This works:

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

because the signature of the + function has been fully specified, so Swift is able to identify the correct + function.

And if we want to assign our new + function to plus:

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

It's really no different than this:

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

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

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

So why the parentheses? Why specify (+) instead of just +?

+ is also a unary operator in Swift.

For example, you can say:

let x = +5

So just trying to say:

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

confuses the compiler because it is treating the + as a unary prefix operator and it is expecting the + to be followed by something else such as 5. By surrounding it with parentheses, the Swift compiler then stops trying to parse + as a unary operator and treats is just as its function name. Even if + weren't a unary prefix operator, Swift would still be expecting values on either side of the +, so the parentheses tell Swift that you aren't providing any inputs to the function, but just want the function itself.

You can refer to the + function without the parentheses in situations where it isn't ambiguous. For example:

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



回答3:


(+) by itself is an operator method. You can declare your own operator like this:

precedencegroup CompositionPrecedence {
    associativity: left
    higherThan: AssignmentPrecedence
}

infix operator •: CompositionPrecedence

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

Usage will be the same:

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


来源:https://stackoverflow.com/questions/52093049/anonymous-function-with-no-curly-braces-and-no-argument-labels

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!