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

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

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

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

 var multResult = calculator.multiply(9834, 2321) 

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

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

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

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

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

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


Вот более общий ответ: функции ведут себя по-разному, если они определены как истинные функции вне classа и определяются как методы. Более того, методы 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

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

 class Calc { init(start: Int) {} init(_ start: String) {} } let c1 = Calc(start: 6) let c2 = Calc("6") 

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

Замечание по совместимости: этот class будет экспортироваться в 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 .

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

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

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

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

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

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

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

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

Свифт наследует много вещей от объектива-c, и это один из них:

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

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

эквивалент в Свифте :

 calculator.multiply(9834, factor2:2321); 

Поскольку ваша функция «умножить» – это метод и как 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).

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

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

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

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

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

 func refresh(obj:Obj, _ method: (Obj)->Void = setValue) { method(element) } func setValue(obj:Obj){ obj.value = "someValue" } refresh(someObj,setValue) 
Давайте будем гением компьютера.