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

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

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

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

var multResult = calculator.multiply(9834, 2321)

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

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

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


person 67cherries    schedule 04.06.2014    source источник
comment
Похоже, компилятор считает, что функция - это метод.   -  person CrimsonChris    schedule 04.06.2014
comment
Унаследован ли класс от класса Какао (например, NSObject)? Он отмечен знаком @objc? Если это так, компилятор предполагает, что он может быть вызван из Objective-C, поэтому его методы должны иметь форму, совместимую с соглашениями о вызове / именовании методов Objective-C.   -  person Ken Thomases    schedule 05.06.2014
comment
нет, это корневой класс (без суперкласса), и он не помечен @objc.   -  person 67cherries    schedule 05.06.2014
comment
можно сделать второе имя аргумента необязательным, просто поставьте _ перед factor2, например func multiply(factor1:Int, _ factor2:Int) {...}.   -  person Hlung    schedule 18.04.2015
comment
Если вам не нравится вводить имена параметров, вы можете заменить func multiply(factor1:Int, factor2:Int) на func multiply(factor1:Int, _ factor2:Int). Это НЕ взлом, а синтаксис официального языка.   -  person Jacob R    schedule 03.07.2015


Ответы (6)


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

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

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


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


Функции

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

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.

person Jean-Philippe Pellet    schedule 04.06.2014
comment
Я добавил примечания по совместимости и правила для init методов. - person Jean-Philippe Pellet; 10.06.2014
comment
Замечательное объяснение. Спасибо! - person sivabudh; 20.06.2014
comment
Но в чем разница между multiply1 (f1: Double) и multiply1 (f1param: Double)? Очевидно, что имя параметра другое, но какое это имеет значение? - person Kokodoko; 28.07.2014
comment
Это не так. Вам просто нужно использовать любое определенное вами имя. - person Jean-Philippe Pellet; 28.07.2014
comment
Еще одна интересная вещь: если вы хотите отображать имя первого параметра при использовании метода, вы должны определить его следующим образом: func method (#firstParameter: type, secondParameter: Type). Выполняя это, при вызове веселья оно появится вот так: self.method (firstParameter: 3, secondParameter: 5) Если вы не добавите #, это будет выглядеть так: self.method (3, secondParameter: 5) - person diegomen; 27.10.2014
comment
Но в чем же смысл этого дизайна? Это пытается помешать нам запутать порядок параметров? Если да, то почему у функции нет такого ограничения? - person MK Yung; 10.06.2015
comment
Это лучше, чем официальная документация. Спасибо за прекрасный прекрасный ответ! - person Bastian; 14.08.2015
comment
Начиная со Swift 2: '#' has been removed from Swift; double up 'param param' to make the argument label the same as the parameter name - person Kof; 29.11.2015
comment
Поскольку вы говорите об инициализации и методах, я могу попросить вас взглянуть на мой вопрос. Я пытаюсь вызвать внешнюю функцию и почти уверен, что мне нужно реализовать инициализацию, чтобы вызвать ее. Я даже назначил награду, и, похоже, никто не справится с этой задачей. Большое тебе спасибо. stackoverflow.com/ questions / 38711226 / - person Edison; 06.08.2016
comment
Когда я пытаюсь пометить параметры функции в typealias, я получаю ошибку компилятора. Это изменилось? - person dcow; 03.04.2018
comment
Начиная с Swift 3, конечно, первый параметр ведет себя так же, как и все остальные, требуя метки аргумента, если функция или метод не использует _ (подчеркивание) вместо внешнего имени. - person Kal; 09.08.2018

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

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

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

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

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

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

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

person Euan M    schedule 30.11.2015
comment
Это хорошее объяснение. Что касается дизайна языка, это не оправдывает добавленную многословность, поскольку мы можем видеть имена параметров и описание метода с помощью Alt + Left Click. - person Cosmin; 14.11.2017

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

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

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

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

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

calculator.multiply(9834, factor2:2321);
person Jiaaro    schedule 04.06.2014
comment
Я планирую принять этот ответ. Я просмотрел документацию, и пример, который они использовали, имеет немного больше смысла, чем мой. - person 67cherries; 04.06.2014
comment
Довольно сбивает с толку людей, которые не работали с Objective C :) - person Kokodoko; 28.07.2014
comment
@Kokodoko, это сбивает с толку людей, которые работали с objective-c 5 лет: P - person Ankit Srivastava; 26.01.2016
comment
Для всех объяснений, приведенных на этой странице, это все еще не имеет особого смысла .... опустить первый параметр, потому что имя функции уже включает его? (это не так, не в примере умножения). Кроме того, если Scratch опускает первый тип параметра, потому что это сбивает с толку, почему они не пропустили все типы? Разве это не сбивает с толку? Извините, я просто разглагольствовал здесь. - person Kokodoko; 26.01.2016

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

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

person Daniel    schedule 04.06.2014
comment
Но это все еще не объясняет, почему можно опустить только имя первого параметра? Почему я не могу просто добавить имя параметра для всех параметров, чтобы все было удобно для чтения? Похоже, это старинный код из старой Objective C, где параметр был частью имени функции. Это привело к тому, что функции стали иметь очень длинные имена. Довольно странно, что нам все еще нужно понимать, как работает Objective C, если мы хотим изучать Swift. - person Kokodoko; 28.07.2014
comment
Первый параметр опущен, потому что стандартные имена методов в какао выглядят так: func multiplyFactor1(factor1: Int, factor2:Int) {} Итак, когда вы вызываете метод, вы пропустили первый параметр, чтобы избежать повторения его имени. myCalculator.multiplyFactor1(2, facto2:2) Читабельнее чем. myCalculator.multiplyFactor1(factor1: 2, facto2:2) - person Daniel; 29.07.2014

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

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

Скретч-пример программы

person user7610    schedule 25.11.2015
comment
Писк - это не детский язык. Это полнофункциональный язык, виртуальная машина, среда графического интерфейса и IDE, которые люди использовали для реализации удобных для детей систем, таких как Scratch, E-Toys и Dr-Geo, визуализатора математических функций для обучения высокому уровню знаний. школьная математика .. Он также использовался для реализации: веб-фреймворка Seaside - фреймворка, который устраняет все проблемы, связанные с людьми, использующими кнопки «назад» и «вперед» в браузере; DabbleDB; и многое другое. - person Euan M; 30.11.2015

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

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