Почему вызов функции требует имя параметра в Swift?

У меня есть эта функция в классе:

func multiply(factor1:Int, factor2:Int) -> Int{
    return factor1 * factor2
}

Я пытаюсь вызвать функцию, используя это:

var multResult = calculator.multiply(9834, 2321)

Проблема заключается в том, что компилятор хочет, чтобы он выглядел следующим образом:

var multResult = calculator.multiply(9834, factor2: 2321)

Почему первая вызывает ошибку?

Ответ 1

Обновление для Swift 2.0. Теперь функции ведут себя одинаково с методами и для обоих по умолчанию:

  • первый параметр не имеет внешнего имени; и
  • другие параметры имеют внешнее имя, идентичное внутреннему имени.

Кроме этого, правила ниже все еще применяются, за исключением того, что сокращенный синтаксис # теперь отсутствует.


Здесь более общий ответ: функции ведут себя по-разному, если они определены как истинные функции вне класса, и когда они определены как методы. Более того, методы init имеют специальное правило.


Функции

Предположим, вы определили это:

func multiply1(f1: Double, f2: Double) -> Double {
    return f1 * f2
}

Имена параметров здесь только локальные для функции и не могут использоваться при вызове функции:

multiply1(10.0, 10.0)

Если вы хотите принудительно использовать именованные параметры при вызове функции, вы можете. Префикс объявления каждого параметра с его внешним именем. Здесь внешнее имя f1 равно f1param, а для f2 мы используем сокращенное обозначение, где мы его префикс #, чтобы указать, что локальное имя также должно использоваться как внешнее имя:

func multiply2(f1param f1: Double, #f2: Double) -> Double {
    return f1 * f2
}

Затем должны использоваться именованные параметры:

multiply2(f1param: 10.0, f2: 10.0)

Методы

Для методов разные. По умолчанию все, кроме первого параметра, называются, как вы обнаружили. Предположим, что мы имеем это, и рассмотрим метод multiply1:

class Calc {
    func multiply1(f1: Double, f2: Double) -> Double {
        return f1 * f2
    }
    func multiply2(f1param f1: Double, f2: Double) -> Double {
        return f1 * f2
    }
    func multiply3(f1: Double, _ f2: Double) -> Double {
        return f1 * f2
    }
}

Затем вы должны использовать имя второго (и следующих, если есть) параметров:

let calc = Calc()
calc.multiply1(1.0, f2: 10.0)

Вы можете принудительно использовать именованный параметр для первого аргумента, указав для него внешнее имя, например, для функций (или префикс его локального имени #, если вы хотите использовать то же самое внешнее имя, что и его локальное имя), Затем вы должны использовать его:

calc.multiply2(f1param: 10.0, f2: 10.0)

Наконец, вы можете объявить внешнее имя _ для других следующих аргументов, указав, что вы хотите вызвать свой метод без использования именованных параметров, например:

calc.multiply3(10.0, 10.0)

Замечание по совместимости: Если вы префикс class Calc с аннотацией @objc, то вы можете использовать его из кода Objective-C и он эквивалентен этому объявлению (смотрите имена параметров)

@interface Calc
- (double)multiply1:(double)f1 f2:(double)f2;
- (double)multiply2WithF1param:(double)f1 f2:(double)f2;
- (double)multiply3:(double)f1 :(double)f2;
@end

Методы инициализации

Правило немного отличается для методов init, где по умолчанию все параметры имеют внешнее имя. Например, это работает:

class Calc {
    init(start: Int) {}
    init(_ start: String) {}
}

let c1 = Calc(start: 6)
let c2 = Calc("6")

Здесь вы должны указать start: для перегрузки, которая принимает Int, но вы должны опустить ее для перегрузки, которая принимает String.

Замечание по совместимости: этот класс будет экспортироваться в Objective-C следующим образом:

@interface Calc
- (instancetype)initWithStart:(NSInteger)start __attribute__((objc_designated_initializer));
- (instancetype)init:(NSString *)start __attribute__((objc_designated_initializer));
@end

Затворы

Предположим, что вы определяете тип замыкания следующим образом:

typealias FancyFunction = (f1: Double, f2: Double) -> Double

Имена параметров будут очень похожи на имена параметров в методе. Вам нужно будет указать имена параметров при вызове закрытия, если вы явно не установили внешнее имя _.

Например, выполнение закрытия:

fund doSomethingInteresting(withFunction: FancyFunction) {
    withFunction(f1: 1.0, f2: 3.0)
}

Как правило, даже если вы им не нравитесь, вы должны, вероятно, попытаться использовать именованные параметры, по крайней мере, когда два параметра имеют один и тот же тип, чтобы их устранить. Я также утверждаю, что хорошо также назвать по крайней мере все параметры Int и Boolean.

Ответ 2

Имена параметров в вызове функции называются именами ключевых слов, и они отсылают их корни к языку Smalltalk.

Классы и объекты часто повторно используются из другого места или составляют часть очень больших сложных систем и не будут иметь активного внимания к обслуживанию в течение длительного времени за раз.

Улучшение ясности и удобочитаемости кода очень важно в этих ситуациях, поскольку код часто заканчивается как единственная документация, когда разработчики находятся под крайним давлением.

Предоставление каждому параметру описательного ключевого слова позволяет сторонам быстро видеть, что цель вызова функции, взглянув на вызов функции, а не углубляться в сам код функции. Это делает подразумеваемое значение параметров явным.

Последним языком для определения имен ключевых слов в вызовах функций является Rust (link) - описывается как "язык системного программирования, который иссякает быстро, предотвращает segfaults и гарантирует безопасность потока."

Высокие системы безотказной работы требуют большего качества кода. Имена ключевых слов позволяют командам разработки и обслуживания намного больше возможностей избегать и улавливать ошибки при отправке неправильного параметра или вызывать параметры не в порядке.

Они могут быть многословными или краткими, но Smalltalkers предпочитают многословную и описательную для краткости и бессмысленности. Они могут себе позволить, потому что их IDE будет делать большую часть такого набора для них.

Ответ 3

так как вы использовали calculator.multiply() в примере кода, я предполагаю, что эта функция является методом объекта calculator.

Swift наследует много вещей от objective-c, и это один из них:

Когда в objective-c вы сделали бы (гипотетически):

[calculator multiply:@9834 factor2:@2321];

эквивалент в Swift:

calculator.multiply(9834, factor2:2321);

Ответ 4

Поскольку ваша функция "multiply" - это метод, и как Objective-c, параметры в методах являются частью имени.

Например, вы можете сделать это.

class Calculator {

    func multiply(factor1:Int, factor2:Int) -> Int{
        return factor1 * factor2
    }

    func multiply(factor1:Int, factor2:Int, factor3:Int) -> Int{
        return factor1 * factor2 * factor3
    }

}

Здесь есть два разных метода, с разными именами, умножьте (factor2) и умножьте (factor2 factor3).

Это правило применяется только к методам, если вы объявляете это как функции вне класса, тогда вызов функции не требует имени параметра.

Ответ 5

Причина историческая. Так он работал в Smalltalk, и он выжил в своих потомках. Squeak, Scratch, Blockly, Цель C и Swift.

Клыкские языки (Squeak, Scratch и Blockly) относятся к нему, потому что начинающие программисты склонны бороться с порядком и порядком параметров. Это была изначальная причина, почему Smalltalk сделал это именно так. Я не знаю, почему ObjC и Swift решили принять конвенцию, но они это сделали.

Пример программы скрипа

Ответ 6

Заметка о передаче в качестве аргумента, который не возвращает значение:

func refresh(obj:Obj, _ method: (Obj)->Void = setValue) {
    method(element)
}
func setValue(obj:Obj){
    obj.value = "someValue"
}
refresh(someObj,setValue)