Swift心得笔记之函数
参数
外部变量名
一般情况下你可以不指定外部变量名,直接调用函数:
funchelloWithName(name:String,age:Int,location:String){
println("Hello\(name).Ilivein\(location)too.Whenisyour\(age+1)thbirthday?")
}
helloWithName("Mr.Roboto",5,"SanFrancisco")
但是在类(或者结构、枚举)中的时候,会自动分配外部变量名(第一个除外),这时候如果还想直接调用就会报错了:
classMyFunClass{
funchelloWithName(name:String,age:Int,location:String){
println("Hello\(name).Ilivein\(location)too.Whenisyour\(age+1)thbirthday?")
}
}
letmyFunClass=MyFunClass()
myFunClass.helloWithName("Mr.Roboto",5, "SanFrancisco")
如果你怀念在OC中定义函数名的方式,可以继续这样定义,比如helloWithName这种,隐藏第一个函数的外部名:
classMyFunClass{
funchelloWithName(name:String,age:Int,location:String){
println("Hello\(name).Ilivein\(location)too.Whenisyour\(age+1)thbirthday?")
}
}
letmyFunClass=MyFunClass()
myFunClass.helloWithName("Mr.Roboto",age:5,location:"SanFrancisco")
如果你实在不想要外部变量名,那么可以用_来代替:
structCelsius{
vartemperatureInCelsius:Double
init(fromFahrenheitfahrenheit:Double){
temperatureInCelsius=(fahrenheit-32.0)/1.8
}
init(fromKelvinkelvin:Double){
temperatureInCelsius=kelvin-273.15
}
init(_celsius:Double){
temperatureInCelsius=celsius
}
}
letboilingPointOfWater=Celsius(fromFahrenheit:212.0)
//boilingPointOfWater.temperatureInCelsius是100.0
letfreezingPointOfWater=Celsius(fromKelvin:273.15)
//freezingPointOfWater.temperatureInCelsius是0.0
letbodyTemperature=Celsius(37.0)
//bodyTemperature.temperatureInCelsius是37.0
对外部参数名的娴熟应用可以极好的抽象初始化过程。可以看看json-swiftlibrary中的应用。
默认参数值
可以在函数定义里写上函数的默认值,这样在调用的时候可以不传这个值:
funcadd(value1v1:Int,value2p1:Int=2)->Int{
returnv1+p1
}
add(value1:2,value2:4) //2+4
add(value1:1) //1+2
如果你没有提供外部参数名,设置默认参数值会自动提供默认参数名。
可变参数
可变参数(VariadicParameters)可以接受一个以上的参数值。比如计算平均数:
funcarithmeticMean(numbers:Double...)->Double{
vartotal:Double=0
fornumberinnumbers{//numbersis[Double]
total+=number
}
returntotal/Double(numbers.count)
}
arithmeticMean(1,2,3,4,5)
arithmeticMean(3,8,19)
如果不止一个参数,需要把可变参数放在最后,否则会报错。应该这样:
funcsumAddValue(addValue:Int=0,numbers:Int...)->Int{
varsum=0
fornumberinnumbers{//numbers===[Int]
sum+=number+addValue
}
returnsum
}
sumAddValue(addValue:2,2,4,5)//(2+2)+(4+2)+(5+2)=17
常量和变量参数
默认参数是常量,无法在函数体中改变参数值。我们可以var一个新的值就行操作,也可以直接在函数定义中加上var避免在函数体中定义新的变量。
比如这一个右对齐函数:
funcalignRight(varstring:String,count:Int,pad:Character)->String{
letamountToPad=count-countElements(string)
ifamountToPad<1{
returnstring
}
letpadString=String(pad)
for_in1...amountToPad{
string=padString+string
}
returnstring
}
letoriginalString="hello"
letpaddedString=alignRight(originalString,10,"-") //"-----hello"
输入输出参数(inout)
在函数体中对变量参数进行的修改并不会改变参数值本身,比如看这个例子:
funcadd(varv1:Int)->Int{
return++v1
}
vara=1
add(a) //2
a //1
如果想通过函数修改原始值需要inout,但是这样是错误的:
funcadd(inoutv1:Int)->Int{
return++v1
}
vara=1
add(a) //2
a //1
在传入的时候,需要加上&标记:
funcadd(inoutv1:Int)->Int{
return++v1
}
vara=1
add(&a) //2
a //1
泛型参数类型
在此借用一下objc.io中的例子来演示泛型参数类型的使用:
//交换两个值的函数
funcvalueSwap<T>(inoutvalue1:T,inoutvalue2:T){
letoldValue1=value1
value1=value2
value2=oldValue1
}
varname1="Mr.Potato"
varname2="Mr.Roboto"
valueSwap(&name1,&name2) //交换字符串
name1//Mr.Roboto
name2//Mr.Potato
varnumber1=2
varnumber2=5
valueSwap(&number1,&number2) //交换数字
number1//5
number2//2
函数类型
在Swift中,函数翻身把歌唱,终于成了一等公民,和其他类型平起平坐。
变量
我们可以定义一个变量,这个变量的类型是函数类型:
funcaddTwoInts(a:Int,b:Int)->Int{
returna+b
}
letanotherMathFunction=addTwoInts
anotherMathFunction(1,2) //3
参数
函数既然是类型的一种,那么显然也是可以作为参数传递的:
funcaddTwoInts(a:Int,b:Int)->Int{
returna+b
}
funcprintMathResult(mathFunction:(Int,Int)->Int,a:Int,b:Int){
println("Result:\(mathFunction(a,b))")
}
printMathResult(addTwoInts,3,5) //将参数2和参数3作为参数传给参数1的函数
返回值
函数也是可以作为结果返回的。比如返回的值是一个参数为Int返回值为Int的函数,就是这样定义:funcfoo()->(Int)->Int。可以看下面这个具体的例子:
funcstepForward(input:Int)->Int{
returninput+1
}
funcstepBackward(input:Int)->Int{
returninput-1
}
funcchooseStepFunction(backwards:Bool)->(Int)->Int{
returnbackwards?stepBackward:stepForward
}
varcurrentValue=3
letmoveNearerToZero=chooseStepFunction(currentValue>0)//根据参数返回stepForward或stepBackward
println("Countingtozero:")
whilecurrentValue!=0{
println("\(currentValue)...")
currentValue=moveNearerToZero(currentValue)
}
println("zero!")
//3...
//2...
//1...
//zero!
别名
用多了会发现在一大串()->中又穿插各种()->是一个非常蛋疼的事情。我们可以用typealias定义函数别名,其功能和OC中的typedef以及shell中的alias的作用基本是一样的。举个例子来看下:
importFoundation
typealiaslotteryOutputHandler=(String,Int)->String
//如果没有typealias则需要这样:
//funcluckyNumberForName(name:String,#lotteryHandler:(String,Int)->String)->String{
funcluckyNumberForName(name:String,#lotteryHandler:lotteryOutputHandler)->String{
letluckyNumber=Int(arc4random()%100)
returnlotteryHandler(name,luckyNumber)
}
luckyNumberForName("Mr.Roboto",lotteryHandler:{name,numberin
return"\(name)'s'luckynumberis\(number)"
})
//Mr.Roboto'sluckynumberis33
嵌套
但是其实并不是所有的函数都需要暴露在外面的,有时候我们定义一个新函数只是为了封装一层,并不是为了复用。这时候可以把函数嵌套进去,比如前面这个例子:
funcchooseStepFunction(backwards:Bool)->(Int)->Int{
funcstepForward(input:Int)->Int{returninput+1}
funcstepBackward(input:Int)->Int{returninput-1}
returnbackwards?stepBackward:stepForward
}
varcurrentValue=-4
letmoveNearerToZero=chooseStepFunction(currentValue<0)
//moveNearerToZeronowreferstothenestedstepForward()function
whilecurrentValue!=0{
println("\(currentValue)...")
currentValue=moveNearerToZero(currentValue)
}
println("zero!")
//-4...
//-3...
//-2...
//-1...
//zero!
柯里化(currying)
柯里化背后的基本想法是,函数可以局部应用,意思是一些参数值可以在函数调用之前被指定或者绑定。这个部分函数的调用会返回一个新的函数。
这个具体内容可以参见Swift方法的多面性中柯里化部分的内容。我们可以这样调用:
classMyHelloWorldClass{
funchelloWithName(name:String)->String{
return"hello,\(name)"
}
}
letmyHelloWorldClassInstance=MyHelloWorldClass()
lethelloWithNameFunc=MyHelloWorldClass.helloWithName
helloWithNameFunc(myHelloWorldClassInstance)("Mr.Roboto")
//hello,Mr.Roboto
多返回值
在Swift中我们可以利用tuple返回多个返回值。比如下面这个例子,返回所有数字的范围:
funcfindRangeFromNumbers(numbers:Int...)->(min:Int,max:Int){
varmaxValue=numbers.reduce(Int.min, {max($0,$1)})
varminValue=numbers.reduce(Int.max, {min($0,$1)})
return(minValue,maxValue)
}
findRangeFromNumbers(1,234,555,345,423)
//(1,555)
而返回值未必都是有值的,我们也可以返回可选类型的结果:
importFoundation
funccomponentsFromUrlString(urlString:String)->(host:String?,path:String?){
leturl=NSURL(string:urlString)
return(url?.host,url?.path)
}
leturlComponents=componentsFromUrlString("http://why/233;param?foo=1&baa=2#fragment")
switch(urlComponents.host,urlComponents.path){
caselet(.Some(host),.Some(path)):
println("host\(host)andpath\(path)")
caselet(.Some(host),.None):
println("onlyhost\(host)")
caselet(.None,.Some(path)):
println("onlypath\(path)")
caselet(.None,.None):
println("Thisisnotaurl!")
}
//"hostwhyandpath/233/param"
以上所述就是本文的全部内容了,希望大家能够喜欢。