使用函数
函数是执行特定任务的代码模块。给定一个函数名称标识,当执行其任务时就可以用这个标识来进行“调用”。
Swift中的每个函数都有一个类型,包括函数的参数类型和返回类型。
1. 函数的声明与调用
当你定义一个函数时,可以为其定义一个或多个命名,定义类型值作为函数的输入(参数),当该函数完成时将传回输出定义的类型(返回类型)。
每一个函数都有一个函数名,用来描述了函数执行的任务。要使用一个函数的功能时,你通过它的名称进行“调用”,并通过他的输入值来匹配函数的参数类型。一个函数的提供的参数必须始终以相同的顺序来作为函数参数列表。
func sayHello(personName: String)->String{
let greeting = "Hello," + personName + "!"
return greeting
}
println(sayHello("hi"))
//Hello,hi!
所有的信息都汇总到函数的定义中,以func关键字为前缀。指定的函数的返回类型是以箭头->(一个连字符一个右尖括号)以及类型的名称作为返回。
该定义描述了函数的作用是什么,它期望接收什么,以及当它完成返回的结果是什么。
println(sayHello("hi"))
通过括号内String类型参数值调用sayHello的函数。
2. 函数的参数和返回值
在Swift中函数的参数和返回值是非常具有灵活性的。可以定义任何东西无论是一个简单的仅仅有一个未命名的参数的函数还是那种具有丰富的参数名称和不同的参数选项的复杂函数。
多输入参数
函数可以有多个输入参数,把它们写到函数的括号内,并用逗号加以分隔。
func halfOpenRangeLength(start: Int, end: Int)->Int{
return end - start
}
println(halfOpenRangeLength(2,1))
//-1
无参函数
函数并没有要求一定要定义输入参数。
func sayHelloWorld()->String{
return "hello world"
}
println(sayHelloWorld())
//hello world
该函数的定义在函数的名称后还需要括号,即使它不带任何参数。当函数被调用时函数名称也要跟着一对空括号。
没有返回值的函数
函数也不需要定义一个返回类型。
func sayGoodbye(personName: String){
println("Goodbye \(personName)!")
}
sayGoodbye("darling")
//Goodbye darling!
因为它并不需要一个返回的值,该函数的定义不包括返回箭头->和返回类型。
提示:严格的说,sayGoodbye即使没有返回值的定义,但确实还返回一个值。函数诶有定义返回类型但返回了一个void返回类型的特殊值。他是一个空的元组,实际上是零个元素的元组,可以写为()。当一个函数调用时,它的返回值可以忽略不计:
func printAndCount(stringToPrint: String)->Int{
println(stringToPrint)
return countElements(stringToPrint)
}
func printWithoutCounting(stringToPrint: String){
printAndCount(stringToPrint)
}
printAndCount("Hello World")
printWithoutCounting("NO ZUO NO DIE")
提示:返回值可以忽略不计,但对一个函数来说,它的返回值即便不实用还是一定会返回的。在函数体底部返回时与定义的返回类型的函数不能相容时,如果试图这样做将导致一个编译错误。
多返回值函数
可以使用一个元组类型作为函数的返回类型返回一个有多个值组成的一个复合作为返回值。
func count(string: String)->(vowels: Int,consonants: Int,others: Int){
var vowels = 0,consonants = 0,others = 0
for character in string{
switch String(character).lowercaseString{
case "a","e","i","o","u":
++vowels
case "b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z":
++consonants
default:
++others
}
}
return (vowels,consonants,others)
}
println(count("abscdf"))
//(1, 5, 0)
3. 函数参数名
所有以上的函数都为参数定义了参数名称:
func someFunction(parameterName: Int){
//function body goes here, and can use parameterName
//to refer to the argument value for that parameter
}
然而,这些参数名仅能在函数本身的主体内使用,在调用函数时,不能使用。这些类型的参数名称被成为本地的参数,因为它们只适用于函数体中使用。
外部参数名
有时当你调用一个函数将每个参数进行命名是非常有用的,以表明你传递给函数的每个参数的目的。
如果你希望用户函数调用你的函数时,提供参数名称,除了设置本地的参数名称,也要为每个参数定义外部参数名称。写一个外部参数名称在它所支持的本地参数名称之前,之间用一个空格来分隔:
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
}
println(join("hello","world",","))
//hello,world
为了使这些字符串值的目的更为清晰,为每个join函数参数定义外部参数名称:
func join(string s1: String,toString s2: String,withJoiner joiner: String)-> String{
return s1 + joiner + s2
}
println(join(string:"fuck" ,toString: "you" ,withJoiner: " "))
//fuck you
注意:考虑到使用外部参数名称的初衷就是为了在别人第一次阅读你的代码时并不知道你函数的目的是什么。但当函数调用时如果每个参数的目的是明确的,毫不含糊的,你并不需要指定外部参数名称。
外部参数名称速记
如果你想为一个函数参数提供一个外部参数名,然而本地参数名已经使用了一个合适的名称了,你不需要为该参数写同样的两次名称。取而代之,写一次名字,并用一个hash符号(#)作为名称的前缀。这告诉Swift使用该名称同时作为本地参数名称和外部参数名称。
func containsCharacter(#string: String,#characterToFind: Character) ->Bool{
for character in string{
if character == characterToFind{
return true
}
}
return false
}
let containsV = containsCharacter(string: "sadafv",characterToFind:"v")
println(containsV)
//true
参数的默认值
可以为任何参数设定默认值来作为函数的定义的一部分。如果默认值已经定义,调用函数时就可以省略该参数的传值。
注意:将使用默认值的参数放在函数的参数列表的末尾。这确保了所有调用函数的非默认参数使用相同的顺序,并明确的表示在每种情况下相同的函数调用。
func join(string s1: String,toString s2: String,withJoiner joiner: String = " ")->String{
return s1 + joiner + s2
}
println(join(string: "fuck",toString: "you"))
//fuck you
有默认值的外部名称参数
在大多数情况下,为所有参数提供一个外部带有默认值的参数的名称是非常有用的(因此要求)。这将确如果当函数被调用时提供的值时参数必须具有明确的目的。
为了使这个过程更容易,当你自己没有提供外部名称时,Swift自动为所有参数定义了缺省的参数外部名称。
func join(s1: String,s2: String,joiner: String = " " )->String{
return s1 + joiner + s2
}
println(join("hello","world"))
//hello world
注意:你可以通过编写一个下划线(_)有选择进行这种行为,而不是一个明确的定义外部参数名称。然而,在适当情况下有默认值的外部名称参数总是优先被使用。
可变参数 (学霸:就是说可变参数在方法中是当数组用的。)
一个可变参数的参数接受0个或者多个指定类型的值。当函数被调用时,可以使用一个可变参数来指定该参数可以传递不同数量的输入值。写可变参数的参数时,需要参数的类型名称后加上 点字符(...)。
传递一个可变参数的参数的值时,函数体中是以提供适当类型的数组的形式存在,比如,一个可变参数的名称为numbers和类型为Double...在函数体内就作为名为numbers类型为Double[]的常量数组。
//计算任意产菇的数字的算术平均值
func arithmeticMean(numbers: Double...)->Double{
var total: Double = 0
for number in numbers{
total += number
}
return total/Double(numbers.count)
}
println(arithmeticMean(1,2,3,4,5))
//3.0
注意:函数可以最多有一个可变参数的参数,而且它必须出现在参数列表的最后,以避免参数函数调用时出现歧义。
如果函数有一个或多个参数使用默认值,而且还具有可变参数,将可变参数放在列表的最末尾的所有默认值的参数之后。
常量参数和变量参数
函数参数的默认值都是常量。试图改变一个函数参数的值会让这个函数体内部产生一个编译错误。
但是,有时函数有一个参数的值的变量副本是非常有用的。可以通过制定一个或多个参数作为变量参数,而不是避免在函数内部为自己定义一个新的变量。变量参数可以是变量而不是常量,并给函数中新修改的参数的值提供一个副本。
在参数名称前用关键字var定义变量参数:
func alignRight(var string: String,count: Int,pad: Character) ->String{
let amountToPad = count - countElements(string)
for _ in 1...amountToPad{
string = pad + string
}
return string
}
let originalString = "Hello"
let paddedString = alignRight(originalString,10,"-")
println(paddedString)
//-----Hello
注意:一个变量参数的变化没有超出每个调用函数,所以对外部函数体是不给见的。变量参数智能存在于函数调用的生命周期里。
输入-输出参数
可变参数,只能在函数本身内改变。如果想有一个函数来修改参数的值,并且想让这些变化在函数调用结束之后,可以定义输入-输出参数来代替。
通过在参数定义的开始添加in out 关键字写用来表明输入-输出参数。一个在输入-输出参数都有一个传递给参数的值,由函数修改后,并从函数返回来代替原来的值。
4. 函数类型
5. 嵌套函数
嵌套函数默认对外界是隐藏的,但仍然可以调用和使用其内部的函数。内部函数也可以返回一个嵌套函数,允许在嵌套函数内的另一个范围内使用。
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")
//-4
//-3
//-2
//-1
//zero