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"
以上所述就是本文的全部内容了,希望大家能够喜欢。