9.2 传递参数

Swift中的函数很灵活,具体体现在传递参数有多种形式。这一节我们介绍几种不同形式的参数。

9.2.1 使用外部参数名

如果我们定义的函数有很多参数,它们又具有相同的数据类型,如果没有清晰的帮助说明,调用者很难知道参数的含义是什么,还记得上一节计算长方形面积的函数rectangleArea吗?我们是这样调用的:

  1. println("320x480的长方形的面积:\(rectangleArea(320, 480))")

从调用的代码中,我们很难看出320和480代表的含义。为了提高程序的可读性,我们可以为函数中的参数提供一个外部参数名,首先看看上一节rectangleArea函数的定义,代码如下:

  1. func rectangleArea(width:Double, height:Double) -> Double {
  2. let area = width * height
  3. return area
  4. }

其中,参数widthheight是函数名的一部分,但是它们只能在函数的内部使用,称为局部参数名。我们还可以为每个参数提供一个可以在函数外部使用的名称,称为外部参数名,修改rectangleArea函数的定义如下:

  1. func rectangleArea(W width:Double, H height:Double) -> Double {
  2. let area = width * height
  3. return area
  4. }

我们在局部参数名之前给一个外部参数名,用空格分隔。定义代码中的WH就是外部参数名。调用代码如下:

  1. println("320x480的长方形的面积:\(rectangleArea(W:320, H:480))")

如果我们提供了外部参数名,那么在函数调用时,必须使用外部参数名,所以W和H不能省略。

外部参数名就像是一个人的“大名”,是让外面人叫的;局部参数名就像是一个人的“小名”,是让家里人叫的。但是也有些人名,在外面和家里是同一个。在rectangleArea函数中,其实局部参数名widthheight听起来也不错,含义也很清晰,程序可读性好。我们就可以把它们既作为局部参数名又作为外部参数名使用。修改rectangleArea函数的定义如下:

  1. func rectangleArea(#width:Double, #height:Double) -> Double {
  2. let area = width * height
  3. return area
  4. }

使用#替代外部参数名,当调用它的时候,可以把局部参数名作为外部参数名使用。调用代码如下:

  1. println("320x480的长方形的面积:\(rectangleArea(width:320, height:480))")

其中,widthheight是局部参数名,但也可以作为外部参数名使用了。

9.2.2 参数默认值

我们在定义函数的时候可以为参数设置一个默认值,当调用函数的时候可以忽略该参数。看下面的一个示例:

  1. func makecoffee(type : String = "卡布奇诺") -> String {
  2. return "制作一杯\(type)咖啡。"
  3. }

上述代码定义了makecoffee函数,可以帮助我们做一杯香浓的咖啡。由于我喜欢喝卡布奇诺,我就把它设置为默认值。在参数列表中,默认值可以跟在参数的后面,通过等号赋值。

在调用的时候,如果调用者传递了参数,则是其传递过来的值,如果没有传递,则是这个默认值。调用代码如下:

  1. let coffee1 = makecoffee(type: "拿铁")
  2. let coffee2 = makecoffee()

其中第①行代码是传递"拿铁"参数,这种参数要求在前面加上参数名,即type: "拿铁"的形式,注意参数名不能省略。第②行代码没有传递参数,因此使用默认值。最后输出结果如下:

  1. 制作一杯拿铁咖啡。
  2. 制作一杯卡布奇诺咖啡。

我们还可以为具有默认值的参数添加外部参数名,也可以使用下划线(_)指定外部参数名,请看下面的示例代码:

  1. func CircleArea(R radius: Double = 30, _ pi: Double = 3.14) -> Double {
  2. let area = radius radius pi
  3. return area
  4. }
  5. println("圆面积:\(CircleArea(R : 50, 3.1415926))")

其中第①行代码是定义函数,第一个参数有外部参数名R,而第二个参数的外部参数名是下划线(_),这样当我们在第②行代码调用该函数的时候,不需要提供第二个参数的外部参数名。

9.2.3 可变参数

Swift中函数的参数个数可以变化,它可以接受不确定数量的输入类型参数,它们具有相同的类型,有点像是传递一个数组。我们可以通过在参数类型名后面加入()的方式来指示这是可变参数。

下面看一个示例:

  1. func sum(numbers: Double...) -> Double {
  2. var total: Double = 0
  3. for number in numbers {
  4. total += number
  5. }
  6. return total
  7. }

上述代码定义了一个sum函数,用来计算传递给它的所有参数之和。参数列表numbers: Double…表示这是Double类型的可变参数。在函数体中参数numbers被认为是一个Double数组,使用for in循环遍历numbers数组集合,计算它们的总和,然后返回给调用者。

下面是两次调用sum函数代码:

  1. sum(100.0, 20, 30)
  2. sum(30, 80)

可以看到每次传递参数的个数是不同的。

9.2.4 参数的传递引用

我们在第5章介绍过,参数传递方式有两种:值类型和引用类型。值类型给函数传递的是参数的一个副本,这样在函数的调用过程中不会影响原始数据。引用类型是把本身数据传递过去,这样在函数的调用过程中会影响原始数据。

在众多数据类型中,只有类是引用类型,其他的数据类型如整型、浮点型、布尔型、字符串、元组、集合、枚举和结构体全部是值类型。

如果一定要将一个值类型参数作为引用传递,也是可以实现的,Swift提供的inout关键字就可以实现。我们看下面的一个示例:

  1. func increment(inout value:Double, amount:Double = 1.0) {
  2. value += amount
  3. }
  4. var value : Double = 10.0
  5. increment(&value)
  6. println(value)
  7. increment(&value, amount:100.0)
  8. println(value)

第①行代码定义了increment函数,这个函数可以计算一个数值的增长,第一个参数value是需要增长的数值,它被设计为inout类型,inout标识的参数被称为输入输出参数,不能使用varlet标识。第二个参数amount是增长量,它的默认值是1.0。函数没有声明返回值类型,函数体中不需要return语句,事实上要返回的数据已经通过参数value传递回来,没有必要通过返回值返回了。

第②行代码我们声明并初始化了Double类型变量value,由于在函数调用过程中需要修改它,因此不能声明为常量。

第③行代码increment(&value)是调用函数increment,增长量是默认值,其中&value(在变量前面加&符号)是传递引用方式,它在定义函数时,参数标识与inout是相互对应的。

第④行代码increment(&value, amount:100.0)也是调用函数increment,增长量是100.0。

上述代码输出结果如下:

  1. 11.0
  2. 111.0

由于是传递引用方式,输出这个结果就很容易解释了。