9.2 传递参数
Swift中的函数很灵活,具体体现在传递参数有多种形式。这一节我们介绍几种不同形式的参数。
9.2.1 使用外部参数名
如果我们定义的函数有很多参数,它们又具有相同的数据类型,如果没有清晰的帮助说明,调用者很难知道参数的含义是什么,还记得上一节计算长方形面积的函数rectangleArea吗?我们是这样调用的:
println("320x480的长方形的面积:\(rectangleArea(320, 480))")
从调用的代码中,我们很难看出320和480代表的含义。为了提高程序的可读性,我们可以为函数中的参数提供一个外部参数名,首先看看上一节rectangleArea函数的定义,代码如下:
func rectangleArea(width:Double, height:Double) -> Double {let area = width * heightreturn area}
其中,参数width和height是函数名的一部分,但是它们只能在函数的内部使用,称为局部参数名。我们还可以为每个参数提供一个可以在函数外部使用的名称,称为外部参数名,修改rectangleArea函数的定义如下:
func rectangleArea(W width:Double, H height:Double) -> Double {let area = width * heightreturn area}
我们在局部参数名之前给一个外部参数名,用空格分隔。定义代码中的W和H就是外部参数名。调用代码如下:
println("320x480的长方形的面积:\(rectangleArea(W:320, H:480))")
如果我们提供了外部参数名,那么在函数调用时,必须使用外部参数名,所以W和H不能省略。
外部参数名就像是一个人的“大名”,是让外面人叫的;局部参数名就像是一个人的“小名”,是让家里人叫的。但是也有些人名,在外面和家里是同一个。在rectangleArea函数中,其实局部参数名width和height听起来也不错,含义也很清晰,程序可读性好。我们就可以把它们既作为局部参数名又作为外部参数名使用。修改rectangleArea函数的定义如下:
func rectangleArea(#width:Double, #height:Double) -> Double {let area = width * heightreturn area}
使用#替代外部参数名,当调用它的时候,可以把局部参数名作为外部参数名使用。调用代码如下:
println("320x480的长方形的面积:\(rectangleArea(width:320, height:480))")
其中,width和height是局部参数名,但也可以作为外部参数名使用了。
9.2.2 参数默认值
我们在定义函数的时候可以为参数设置一个默认值,当调用函数的时候可以忽略该参数。看下面的一个示例:
func makecoffee(type : String = "卡布奇诺") -> String {return "制作一杯\(type)咖啡。"}
上述代码定义了makecoffee函数,可以帮助我们做一杯香浓的咖啡。由于我喜欢喝卡布奇诺,我就把它设置为默认值。在参数列表中,默认值可以跟在参数的后面,通过等号赋值。
在调用的时候,如果调用者传递了参数,则是其传递过来的值,如果没有传递,则是这个默认值。调用代码如下:
let coffee1 = makecoffee(type: "拿铁") ①let coffee2 = makecoffee() ②
其中第①行代码是传递"拿铁"参数,这种参数要求在前面加上参数名,即type: "拿铁"的形式,注意参数名不能省略。第②行代码没有传递参数,因此使用默认值。最后输出结果如下:
制作一杯拿铁咖啡。制作一杯卡布奇诺咖啡。
我们还可以为具有默认值的参数添加外部参数名,也可以使用下划线(_)指定外部参数名,请看下面的示例代码:
func CircleArea(R radius: Double = 30, _ pi: Double = 3.14) -> Double { ①let area = radius * radius * pireturn area}println("圆面积:\(CircleArea(R : 50, 3.1415926))") ②
其中第①行代码是定义函数,第一个参数有外部参数名R,而第二个参数的外部参数名是下划线(_),这样当我们在第②行代码调用该函数的时候,不需要提供第二个参数的外部参数名。
9.2.3 可变参数
Swift中函数的参数个数可以变化,它可以接受不确定数量的输入类型参数,它们具有相同的类型,有点像是传递一个数组。我们可以通过在参数类型名后面加入(…)的方式来指示这是可变参数。
下面看一个示例:
func sum(numbers: Double...) -> Double {var total: Double = 0for number in numbers {total += number}return total}
上述代码定义了一个sum函数,用来计算传递给它的所有参数之和。参数列表numbers: Double…表示这是Double类型的可变参数。在函数体中参数numbers被认为是一个Double数组,使用for in循环遍历numbers数组集合,计算它们的总和,然后返回给调用者。
下面是两次调用sum函数代码:
sum(100.0, 20, 30)sum(30, 80)
可以看到每次传递参数的个数是不同的。
9.2.4 参数的传递引用
我们在第5章介绍过,参数传递方式有两种:值类型和引用类型。值类型给函数传递的是参数的一个副本,这样在函数的调用过程中不会影响原始数据。引用类型是把本身数据传递过去,这样在函数的调用过程中会影响原始数据。
在众多数据类型中,只有类是引用类型,其他的数据类型如整型、浮点型、布尔型、字符串、元组、集合、枚举和结构体全部是值类型。
如果一定要将一个值类型参数作为引用传递,也是可以实现的,Swift提供的inout关键字就可以实现。我们看下面的一个示例:
func increment(inout value:Double, amount:Double = 1.0) { ①value += amount}var value : Double = 10.0 ②increment(&value) ③println(value)increment(&value, amount:100.0) ④println(value)
第①行代码定义了increment函数,这个函数可以计算一个数值的增长,第一个参数value是需要增长的数值,它被设计为inout类型,inout标识的参数被称为输入输出参数,不能使用var或let标识。第二个参数amount是增长量,它的默认值是1.0。函数没有声明返回值类型,函数体中不需要return语句,事实上要返回的数据已经通过参数value传递回来,没有必要通过返回值返回了。
第②行代码我们声明并初始化了Double类型变量value,由于在函数调用过程中需要修改它,因此不能声明为常量。
第③行代码increment(&value)是调用函数increment,增长量是默认值,其中&value(在变量前面加&符号)是传递引用方式,它在定义函数时,参数标识与inout是相互对应的。
第④行代码increment(&value, amount:100.0)也是调用函数increment,增长量是100.0。
上述代码输出结果如下:
11.0111.0
由于是传递引用方式,输出这个结果就很容易解释了。
