为什么函数调用需要Swift中的参数名?

我在一个类中有这个函数:

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的更新 :现在函数的行为与方法相同,并且默认情况下:

  • 第一个参数没有外部名称; 和
  • 其他参数具有与内部名称相同的外部名称。

除此之外,下面的规则仍然适用,除了#简写语法现在已经消失了。


下面是一个更一般的答案:当定义为类之外的真实函数,定义为方法时,函数的行为会有所不同。 而且,init方法有一个特殊的规则。


function

假设你定义了这个:

 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) 

互操作性注意事项:如果用@objc注解的class Calc前缀,则可以从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 

closures

假设你定义一个闭包types如下:

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

参数名称的行为与方法中的行为非常相似。 调用闭包时,必须为参数提供名称,除非明确将外部名称设置为_。

例如,执行闭包:

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

作为一个经验法则:即使你不喜欢它们,也应该至less在两个参数具有相同types的情况下继续使用命名参数,以消除它们的歧义。 我也会争辩说,至less命名所有IntBoolean参数也是很好的。

函数调用中的参数名称称为关键字名称,它们追溯到Smalltalk语言。

类和对象通常在其他地方被重复使用,或者形成非常大的复杂系统的一部分,并且一次不会长时间保持积极的维护注意力。

在这些情况下,提高代码的清晰度和易读性是非常重要的,因为在开发人员面临最后期限的压力时,代码通常会成为唯一的文档。

为每个参数提供一个描述性的关键字名称,使维护人员能够通过浏览函数调用来快速查看函数调用的目的,而不是深入研究函数代码本身。 它使参数的含义显式化。

在函数调用中采用参数的关键字名称的最新语言是Rust(链接) – 被描述为“系统编程语言运行得非常快,可以防止段错误,并保证线程安全。

高运行时间系统需要更高的代码质量。 关键字名称允许开发和维护团队有更多的机会避免发送错误的参数或调用参数。

他们可以罗嗦或简洁,但Smalltalkers喜欢罗嗦和描述性简洁和毫无意义。 他们可以负担得起,因为他们的IDE将为他们做大部分的打字工作。

由于您在示例代码中使用了calculator.multiply() ,因此我假定此函数是calculator对象的一种方法。

Swift从objective-cinheritance了很多东西,这就是其中之一:

在objective-c中,你会做(假设):

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

在Swift中相当于 :

 calculator.multiply(9834, factor2:2321); 

因为你的“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)和multiply(factor2 factor3)。

这个规则只适用于方法,如果你声明这是一个类之外的函数,那么函数调用不需要参数名称。

原因是历史的。 这就是它在Smalltalk中的工作原理,并在其后代中幸存下来。 吱吱声, 从头开始 , 块状 ,Objective C和Swift。

开玩笑的语言(Squeak,Scratch和Blockly)持有这种语言,因为开始的程序员倾向于与参数和参数顺序进行斗争。 这就是为什么Smalltalk这样做的原因。 我不知道为什么ObjC和Swift决定通过这个公约,但他们做到了。

抓痕示例程序

有关将方法作为不返回值的parameter passing的注意事项:

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