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 * height
return area
}
其中,参数width
和height
是函数名的一部分,但是它们只能在函数的内部使用,称为局部参数名。我们还可以为每个参数提供一个可以在函数外部使用的名称,称为外部参数名,修改rectangleArea
函数的定义如下:
func rectangleArea(W width:Double, H height:Double) -> Double {
let area = width * height
return 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 * height
return 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 pi
return area
}
println("圆面积:\(CircleArea(R : 50, 3.1415926))") ②
其中第①行代码是定义函数,第一个参数有外部参数名R,而第二个参数的外部参数名是下划线(_
),这样当我们在第②行代码调用该函数的时候,不需要提供第二个参数的外部参数名。
9.2.3 可变参数
Swift中函数的参数个数可以变化,它可以接受不确定数量的输入类型参数,它们具有相同的类型,有点像是传递一个数组。我们可以通过在参数类型名后面加入(…
)的方式来指示这是可变参数。
下面看一个示例:
func sum(numbers: Double...) -> Double {
var total: Double = 0
for 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.0
111.0
由于是传递引用方式,输出这个结果就很容易解释了。