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

时间:2014-06-04 19:25:42

标签: methods swift named-parameters

我在课堂上有这个功能:

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

我尝试使用此函数调用该函数:

var multResult = calculator.multiply(9834, 2321)

问题是编译器希望它看起来更像这样:

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

为什么第一个会导致错误?

6 个答案:

答案 0 :(得分:116)

Swift 2.0的更新:现在函数的行为与方法完全相同,默认情况下两者的行为相同:

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

除此之外,以下规则仍适用,但#速记语法现已消失。


这里有一个更一般的答案:函数在定义为类外的真函数时定义为方法时表现不同。而且,init方法有一个特殊的规则。


功能

假设你定义了这个:

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

参数名称此处只有 local ,并且在调用函数时不能使用:

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)
}

根据经验:即使您不喜欢它们,您也应该尝试至少在两个参数具有相同类型的情况下继续使用命名参数,以便消除它们的歧义。我还认为,至少为所有IntBoolean参数命名也是一件好事。

答案 1 :(得分:4)

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

类和对象经常从其他地方重复使用,或者构成非常大的复杂系统的一部分,并且一次不会长时间有效地维护。

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

为每个参数提供一个描述性的关键字名称,使维护者可以通过浏览函数调用快速查看函数调用的目的,而不是深入研究函数代码本身。它使参数的隐含含义明确。

在函数调用中采用参数的关键字名称的最新语言是Rust (link) - 被描述为“一种运行速度极快,防止段错误并保证线程安全的系统编程语言。”

高正常运行时间系统需要更高的代码质量。关键字名称使开发和维护团队有更多机会避免从发送错误参数或调用参数乱序中捕获错误。

他们可能是罗嗦或简洁,但Smalltalkers更喜欢冗长和描述性的简洁和无意义。他们可以负担得起,因为他们的IDE会为他们做大量的打字。

答案 2 :(得分:2)

因为您在示例代码中使用了calculator.multiply()我假设此函数是calculator对象的方法。

Swift从objective-c继承了很多东西,这是其中之一:

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

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

equivalent in Swift是:

calculator.multiply(9834, factor2:2321);

答案 3 :(得分:1)

因为你的“乘法”函数是一个方法,就像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)。

此规则仅适用于方法,如果将其声明为类外的函数,则函数调用不需要参数名称。

答案 4 :(得分:0)

原因是历史性的。这就是它在Smalltalk中的工作方式,并在它的后代中幸存下来。吱吱声,ScratchBlockly,目标C和斯威夫特。

Kiddy语言(Squeak,Scratch和Blockly)坚持使用它,因为初学程序员倾向于与arity和参数顺序斗争。这就是Smalltalk这样做的原因。我不知道为什么ObjC和Swift决定采用这个惯例,但他们确实如此。

Scratch example program

答案 5 :(得分:0)

关于将方法作为参数传递而不返回值的说明:

RowDeleting