Swift中通过func关键字定义方法,->后面紧跟函数返回值类型。
func sayHello(personName:String) -> String {
let greeeting = "Hello," + personName + "!"
return greeeting
}
sayHello("Anna")
sayHello("Brian")
func sayHelloAgain(personName:String) -> String {
return "Hello," + personName + "!"
}
sayHello("Anna")
func halfOpenRangeLength(start:Int,end:Int) ->Int{
return end - start
}
halfOpenRangeLength(1, 10)
func sayHelloWorld() -> String{
return "Hello,world."
}
sayHelloWorld()
func sayGoodbye(personName:String){
println("Goodbye,\(personName)!")
}
sayGoodbye("Dave")
严格地说,sayGoodbye函数确实还返回一个值,即使没有定义返回值。没有定义返回类型的函数返回了一个Void类型的特殊值。这仅是一个空元组,这里边没有元素,可以被写成()。
当调用函数时,函数的返回值可以忽略:
func printAndCount(stringToPrint:String) -> Int{
println(stringToPrint)
return countElements(stringToPrint)
}
func printWithoutCounting(stringToPrint:String){
printAndCount(stringToPrint)
}
printAndCount("Hello,wirld")
printWithoutCounting("Hello,wirld")
返回值可以忽略,但一个定义了返回值的函数则必须有返回值。对于一个定义了返回类型的函数来说,如果没有返回值,那么将不允许控制流离开函数的底部。如果试图这样做将出现一个编译时错误。
你可以使用一个元组类型作为函数的返回类型,来返回一个由多个值组成的复合返回值。
func minMax(array:[Int]) -> (min:Int,max:Int){
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count]{
if value < currentMin{
currentMin = value
}else if value > currentMax{
currentMax = value
}
}
return (currentMin,currentMax)
}
let bounds = minMax([8,-6,2,109,3,17])
println("min is \(bounds.min) and max is \(bounds.max)")
注意:此时元组的成员不需要被命名,元组是从函数中返回的,因为它们的名字已经被指定为函数的返回类型的一部分。
如果函数将返回一个可能为没有任何值的元组,你可以定义返回值为可选元组类型。注意(Int,Int)?不同于(Int?,Int?),前者是元组可选,可以是没有任何值,而后者是元组的成员是可选类型。
上面的minMax方法没有对传入的数组是否为空作安全检查,如果传入的数组为空,会导致运行时错误。可以改写为:
func minMaxWithSafeCheck(array:[Int]) -> (min:Int,max:Int)?{
if array.isEmpty{
return nil
}
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count]{
if value < currentMin{
currentMin = value
}else if value > currentMax{
currentMax = value
}
}
return (currentMin,currentMax)
}
用可选绑定来检查这个方法是返回了正确的元组还是nil。
if let bounds = minMaxWithSafeCheck([8,-6,2,109,3,17]){
println("min is \(bounds.min) and max is \(bounds.max)")
}
func someFunction(parameterName: Int) {
// function body goes here, and can use parameterName
// to refer to the argument value for that parameter
}
像这样的函数,这些参数名的仅能在函数本身的主体内使用,不能在调用函数时使用。这种形参类型名称被称之为本地形参名(local parameter name),因为它们只能在函数的主体中使用。
a、外部形参名
有时当你调用一个函数将每个形参进行命名是非常有用的,以表明你把每个实参传递给函数的目的。
如果你希望使用你函数的人在调用函数时提供形参名称,那除了本地形参名外,你还要为每个形参定义一个外部形参名称。你写一个外部形参名称在它所支持的本地形参名称之前,之间用一个空格来分隔:
func someFunction(externalParameterName localParameterName: Int) {
// function body goes here, and can use localParameterName
// to refer to the argument value for that parameter
}
注意:如果您为形参提供一个外部形参名称,那么外部形参名必须在调用时使用。
例如下面一个连接字符串的方法,调用的时候就不太清楚函数的用意:
func join(s1:String,s2:String,joiner:String) ->String {
return s1 + joiner + s2
}
join("hello", "world", ",")
可以通过函数的外部形参名把方法改成下面的这样,这样调用者就比较清楚函数的用意了:
func join(string s1:String,toString s2:String,withJoiner joiner:String) -> String{
return s1 + joiner + s2
}
join(string: "hello", toString: "world", withJoiner: ",")
在这个版本的join函数中,第一个形参的外部名称string,本地名称s1;第二个形参的外部名称toString,本地名称s2;第三个形参的外部名称是withJoiner,本地名称为joiner。
b、外部形参名速记
如果你想为一个函数提供一个外部形参名,然而本地形参名已经使用了一个合适的名称了,那你就不需要两次书写该形参的名称。相反,你可以写一次名字,并用一个hash符号(#)作为名称的前缀。这就告诉Swift使用名称相同的本地行参名称和外部形参名称。
func containsCharacter(#string:String,#characterToFind:Character) -> Bool{
for characer in string{
if characer == characterToFind{
return true
}
}
return false
}
let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v")
//true
c、默认形参值
你可以为任何形参定义默认值以作为函数定义的一部分。如果已经定义了默认值,那么调用函数时就可以省略该形参。
注意:请在函数形参列表的末尾放置带默认值的形参。这将确保所有函数调用都使用顺序相同的无默认值实参,并让在每种情况下清晰地调用相同的函数。
func join1(string s1:String,toString s2:String,withJoiner joiner:String = " ") -> String{
return s1 + joiner + s2
}
join1(string: "Hello", toString: "world", withJoiner: "-")
join1(string: "Hello", toString: "world")
如果在join函数调用时为joiner提供了字符串值,那么该字符串值可以用来连接两个字符串。如果函数调用时没有为joiner提供值,就会使用单个空格(" ")的默认值。
//多个默认形参
func join111(string s1:String,toString s2:String,withJoiner joiner:String = " ",test:String = "111") -> String{
return s1 + joiner + s2 + test
}
join111(string: "Hello", toString: "world", withJoiner: "-",test:"112")
join111(string: "Hello", toString: "world",test:"12")//竟然可以跳过中间的形参
d、有默认值的外部形参名
在大多数情况下,为所有形参提供一个带默认值的外部名是非常有用的(因此要求)。如果在调用函数的时候提供了一个值,那么这将确保形参对应的实参有着明确的目的。
为了使这个过程更容易,当你自己没有提供外部名称时,Swift将为你定义的任何默认形参提供一个自动外部名。这个自动外部名和本地名一样,就像你已经在本地名前添加了hash符号(#)一样。
func join2(s1:String,s2:String,joiner:String = " ") -> String{
return s1 + joiner + s2
}
join2("hello", "world", joiner: "-")
注意:在定义形参时,你可以通过使用下划线(_)来代替显示外部名称。不过在适当的情况下,带有默认值形参的外部名通常是优先推荐的。
e、可变形参
一个可变形参可接受零个或多个指定类型的值。当函数被调用时,你可以使用可变形参来指定--形参可以用来传递任意数量的输入值。可通过在形参的类型名后边插入三个点符号(...)来编写可变形参。
传递至可变形参的值在函数主体内是以适当类型的数组存在的。例如,一个可变参数的名称为numbers和类型为Double...在函数体内就作为名为numbers类型为Double[]的常量数组。
func arithmeticMean(numbers:Double...) ->Double{
var total:Double = 0
for number in numbers{
total += number
}
return total/Double(numbers.count)
}
arithmeticMean(1,2,3,4,5)
arithmeticMean(3,8.25,18.75)
注意:函数最多可以有一个可变形参,而且它必须出现在参数列表的最后,以避免使用多个形参调用函数引发歧义。如果你的函数有一个或多个带有默认值的形参,并且还有可变形参,请将可变形参放在所有默认形参之后,也就是的列表的最末尾。
f、常量形参和变量形参
函数的形参默认是常量。试图在函数体内改变函数形参的值会引发一个编译时错误。这意味着你不能错误地改变形参的值。
但是有时候,函数有一个形参值的变量副本是非常有用的。您可以指定一个或多个形参作为变量形参,从而避免在函数内部为自己定义一个新的变量。变量参数是变量而非常量,并给函数一个可修改的形参值副本。
在参数名称前用关键字var定义变量参数:
func alignRight(var string:String,count:Int,pad:Character) ->String{
let amountToPad = count - countElements(string)
if amountToPad < 1{
return string
}
let padString = String(pad)
for _ in 1...amountToPad{
string = padString + string
}
return string
}
let originalString = "hello"//hello
let paddedString = alignRight(originalString, 10, "-")//-----hello
这个例子定义了一个新函数叫做alignRight,用于将一个输入字符串和更长的输出字符串右边缘对齐。所有左侧的空白使用指定的占位符来填充。在这个例子中,字符串"hello"被转化为字符串"-----hello"。
alignRight函数把输入的形参字符串定义成一个变量形参。这意味着字符串现在可以作为一个本地变量,用传入的字符串值初始化,并且可以在函数体中进行相应操作。
函数首先要找出有多少字符需要被添加到字符串的左侧,从而在整个字符串中靠右对齐。这个值存储在本地常量amountToPad中。该函数然后将填充字符的amountToPad个字符拷贝到现有的字符串的左边,并返回结果。
注意:你对变量形参所做的改变不会比调用函数更持久,并且在函数体外是不可见的。变量形参仅存在于函数调用的声明周期中。
g、inout形参
如上描述,变量形参只能在函数本身内改变。如果你想让函数改变形参值,并想要在函数调用结束后保持形参值的改变,那你可以把形参定义为in-out形参。
通过在形参定义的开始添加inout关键字来编写in-out形参。In-Out形参有一个传递至函数的值,由函数修改,并从函数返回来替换原来的值。
你只能传递一个变量作为inout形参对应的实参。你不能传递一个常量或者字面量作为实参,因为常量和字面量不能被修改。当你把变量作为实参传递给inout形参时,需要在变量前添加 & 符号,以表明它可以被函数修改。
提示:inout参数不能有默认值,可变参数的参数也不能被标记为inout。如果您标记参数为inout,它不能同时被标记为var或let。
func swapTwoInts(inout a:Int,inout b:Int){
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt),and anotherInt is now \(anotherInt)")
传入的参数前需要加上&符号!
注意:inout形参不同于从函数返回一个值。上边swapTwoInts例子没有定义返回类型或者返回值,但它仍然会修改someInt和anotherInt的值。对函数来说,inout形参是一个影响函数主体范围外的可选方式。
每一个函数都有特定的函数类型,由函数的形参类型和返回类型组成。例如:
//(Int,Int) -> Int 类型
func addTwoInts(a:Int,b:Int) -> Int{
return a + b
}
func multiplyTwoInts(a:Int,b:Int) -> Int{
return a * b
}
//() -> ()类型 或者"没有形参的函数,并返回void。"没有指明返回值的函数通常会返回void,在swift中相当于一个空元组,显示为()。
func printHellWorld(){
print("Hello,world")
}
1、使用函数类型
在swift中您可以像任何其他类型一样的使用函数类型。
var mathFunction: (Int,Int) -> Int = addTwoInts
println("Reslut:\(mathFunction(2,3))")//5
只要函数类型一致,就可以赋值给同一变量:
mathFunction = multiplyTwoInts
println("Reslut:\(mathFunction(2,3))")//6
可以让Swift自己推断函数类型:
let anotherMathFunction = addTwoInts //(Int,Int) -> Int 类型
2、函数类型作为函数形参类型
可以使用一个函数类型,如(Int, Int)->Int作为另一个函数的形参类型。这使你预留了一个函数的某些方面的函数实现,让调用者提供的函数时被调用。
func printMathResult(mathFunction:(Int,Int)->Int,a:Int,b:Int){
println("Result:\(mathFunction(a,b))")
}
printMathResult(addTwoInts, 3, 5)//8
该函数真正实现了什么并不重要--它只关心函数的类型是正确的。这使得printMathResult以一种安全类型的方式把自身的功能转换至函数的调用者。
3、函数类型作为函数的返回类型
你可以将一个函数类型作为另一个函数的返回类型。你可以在返回函数的返回箭头(->) 后立即编写一个完整的函数类型来实现。
func stepForward(input:Int)->Int{
return input + 1
}
func stepBackward(input:Int)->Int{
return input - 1
}
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
return backwards ? stepBackward:stepForward //此处注意?要与backwards相隔一个空格
}
var currentValue = 3
let moveNearerToZero = chooseStepFunction(currentValue > 0)//moveNearerToZero引用了stepBackward()函数
println("Counting to zero:")
while currentValue != 0{
println("\(currentValue)...")
currentValue = moveNearerToZero(currentValue)
}
println("zero!")
迄今为止所有你在本章中遇到函数都是全局函数,在全局作用域中定义。其实你还可以在其他函数体中定义函数,被称为嵌套函数。
嵌套函数默认对外界是隐藏的,但仍然可以通过它们包裹的函数调用和使用它。enclosing function也可以返回一个嵌套函数,以便在其他作用域中使用嵌套函数。
你可以重写上面的chooseStepFunction例子使用并返回嵌套函数:
func chooseStepFunction(backwards:Bool) -> (Int) -> Int{
func stepForward(input:Int) -> Int{
return input + 1
}
func stepBackward(input:Int) -> Int{
return input - 1
}
return backwards ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(currentValue > 0)
while currentValue != 0{
println("\(currentValue)...")
currentValue = moveNearerToZero(currentValue)
}
println("zero!")
这一章介绍了Swift的函数,它跟C差不多风格,但是跟OC就差不大了。它通过关键字func定义,参数包含在()里面,函数返回值类型跟在->后面。重点是函数的形参名这部分内容的介绍。最神奇的就是函数的形参可以默认值、外部形参名、可变形参和inout形参。在调用函数的时候,可以忽略有默认值的形参,有多个有默认值的形参的时候,调用函数的时候,还可以跳过中间某个有默认值的形参,非常的灵活。现总结下重点:
1、当函数有多个返回值时,可以使用元组。考虑到安全问题,建议使用可选元组,这样很方便拿到对应的返回值。
2、为了使函数的调用者更清楚地知道函数的功能,可以使用外部形参名。方法很简单,只需要在对应的本地形参名前面再加上对应的外部形参名就可以了。最简单的方式是在本地形参名前面加上#,让外部形参名和本地形参名一样。我更喜欢后者,很方便,而且减少代码量。
3、可以为形参设置默认值,奇怪的是Swift可以这样调用同一个函数: join1(string: "Hello", toString: "world", withJoiner: "-")
join1(string: "Hello", toString: "world")后者不加,withJoiner:竟然也可以。注意设置了默认值的形参要放在形参列表的最后。
4、Swift会给有默认值的形参提供外部形参名,跟本地形参名一样。所以,有默认值的形参,不需要再设置外部形参名,也不用加#。
5、函数支持可变形参,且只能有一个可变形参,可变形参相当于一个数组,传入的参数类型必须是一样的。可变形参必须放在函数参数列表的最后。
6、函数的形参默认是常量,要在函数内修改形参,需要在定义函数的时候显示的将形参定义为var类型。
7、如果要在函数内修改的形参对函数外部起作用,需要用inout关键字申明函数参数,inout跟var或let不能共存。调用函数,传入实参给inout变量的时候必须传入变量,不能传入不可改变的常量和字面值。且必须记得在变量前加上&符号。
8、函数都是有类型的,函数是可以嵌套的。
参考:
1、The Swift Programming Language