Анонимная функция без фигурных скобок и без меток аргументов? - PullRequest
0 голосов
/ 30 августа 2018

Я видел код на другой вопрос , который, кажется, создает анонимную функцию (выражение закрытия) с необычным синтаксисом:

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

Я понимаю левую сторону - она ​​объявляет константу типа (Int, Int) -> Int (функция, которая принимает два целых числа и возвращает целое число). Но что такое (+)? Как он может объявить функцию без фигурных скобок и как он ссылается на два аргумента, когда нет никаких меток аргументов любого вида?

Функция принимает два аргумента, складывает их вместе и возвращает результат. Если я заменю оператор + другим оператором (скажем, *), операция изменится. Так это что-то вроде сокращения для {$0 + $1}? Если да, то какова логика этого сокращения?

Ответы [ 3 ]

0 голосов
/ 30 августа 2018

+ - это оператор 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, +)
0 голосов
/ 30 августа 2018

(+) сам по себе является операторным методом. Вы можете объявить свой собственный оператор следующим образом:

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))")
0 голосов
/ 30 августа 2018

На самом деле, это не сокращение.

plus - это переменная типа (Int, Int) -> Int. Вы можете назначить ему любой объект этого типа (или любой из его подтипов). Буквальное лямбда-замыкание, безусловно, относится к этому типу, но на самом деле подойдет и именованная функция или метод. И это именно то, что здесь происходит.

Это присвоение переменной объекта объекта метода оператора с именем +.

Это как бы неявно упоминается в главе Closures в руководстве по языку :

Методы оператора

На самом деле есть даже более короткий способ написания выражения замыкания выше. Тип String Swift определяет свою специфическую для строки реализацию оператора «больше» (>) как метод, который имеет два параметра типа String и возвращает значение типа Bool. Это точно соответствует типу метода, необходимому для метода sorted(by:). Следовательно, вы можете просто передать оператор «больше, чем», и Swift определит, что вы хотите использовать его специфическую для строки реализацию:

reversedNames = names.sorted(by: >)

Итак, код делает присвоение метода оператора + переменной plus. + - это просто имя функции, назначенной переменной. Никаких волшебных сокращений.

Вы бы удивились, увидев это?

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