苹果新的编程语言 Swift 语言进阶(六)--函数和闭包

一 、函数

        1.1、 函数的定义和调用

        函数的定义以funckeyword作为前缀,接着是函数名字,接着跟着一个能够带有參数。也能够不带參数的圆括号。接着用-> 指示函数的返回类型。

函数运行体用一对大括号{}包围。例如以下定义了一个函数名为sayHello的函数。该函数包括一个名字为personName,类型为String的输入參数。

func sayHello(personName:String) -> String {

   let greeting ="Hello, " +personName +"!"

   return greeting

}

   你能使用上面定义的函数名sayHello并在圆括号里包括一个传给该函数的一个字符串參数值来调用该函数。

比如   

   sayHello(“Anna”)。


   Swift也能够定义一个不带返回值的函数,如

func sayGoodbye(personName:String) {

   println("Goodbye,(personName)!")

}

   注: 未定义返回值函数的返回类型为Void,是一个空的多元组(包括零个元素),写作()。

   Swift中能够使用一个多元组类型作为函数的返回类型,以便从一个函数返回多个值。

func count(string:String) -> (vowels:Int,consonants:Int,others:Int) {

    return (vowels,consonants,others)

}

   以上定义的函数中的參数在圆括号里以parameterName:parameterType形式定义,以这样的形式定义的參数称为本地參数,即仅仅能在函数运行体内部使用,不能在函数调用时使用。

   Swift支持为一个函数的每个參数命名一个外部參数。外部參数在函数本地參数的前面声明。例如以下所看到的:

func join(string s1:String,toString s2:String,withJoiner joiner:String)

    ->String {

       return s1 +joiner +s2

}

   为一个函数命名的外部參数能够在也仅仅能在函数调用时使用。

例如以下所看到的在调用上面的函数时加上以上为參数命名的外部參数名字。

     join(string:"hello",toString:"world",withJoiner:", “)

   为函数參数命名一个外部參数主要是为了使函数的參数意义更加清晰。

   假设一个函数的本地參数名字已经比較适当。这时能够在函数的本地參数名字前加上一个’#’符号。指示该參数名字即用作本地參数名,又用于外部參数名。

如:

func containsCharacter(#string:String, #characterToFind:Character) ->Bool {

 

}

   Swift中,你还能在函数定义中为函数的随意參数定义一个默认值。假设为一个函数的某个參数提供了默认值。在调用该函数时就行不传送定义默认值的參数。

   定义默认值的參数要放到函数參数列表的末端,未定义默认值的參数要放到函数參数列表的前面,以便调用函数的形式统一。


fund join(string s1:String,toString s2:String,

   withJoiner joiner:String =" ") ->String {

       return s1 +joiner +s2

}

   以上join函数为joiner參数定义了默认參数。因此能够用例如以下方式调用该函数,第三个參数没有被传递,函数内部使用其默认值:

     join(string:"hello",toString:"world")

     // returns "hello world”

   多数情况下为带默认值的參数提供一个外部名字是必要和实用的。这样可以在调用该函数时使函数表达的功能更加清晰。

   因此针对这样的情况,假设不为默认參数提供一个外部名字。Swift自己主动为其分配一个外部參数名。自己主动分配的外部參数名与其本地名字同样。例如以下样例:

func join(s1:String,s2:String,joiner:String =" ") ->String {

   return s1 +joiner +s2

}

   如上函数Swift自己主动为带默认值的joiner參数提供了一个外部參数名,其名字和其本地名字joiner同样。

   因此调用该函数时必须为其提供外部名字,从而使函数參数的表达的意思更加清楚和没有歧义。

          join("hello","world",joiner:“-")

 Swift 也支持函数带有可变參数。用来接受零个或多个特定类型的參数。可变參数在參数类型后面加...符号标识。传给函数的可变參数在函数体内作为一个适当类型的数组使用。

     例如以下样例展示了一个使用可变參数的函数样例。

func arithmeticMean(numbers:Double...) -> Double {

   var total:Double =0

   for number in numbers {

       total +=number

    }

   return total /Double(numbers.count)

}

    为了避免歧义,函数的可变參数总是出如今函数參数列表的最后。

    函数參数默认是常量类型,不须要加let 标识。但 Swift 支持在函数定义中使用varkeyword定义变量參数,变量參数也仅仅在函数运行体内部有效和使用。

func alignRight(var string:String,count:Int,pad:Character) ->String {

   let amountToPad =count -countElements(string)

   for in ... amountToPad {

       string =pad +string

    }

   return string

}

   Swift 为了支持一个函数体内部对參数改动的值仍可以在函数调用结束后被外部所用。定义了一种in-out 參数。

   Swift在參数定义的前面加入一个inoutkeyword来定义一个in-out 參数。in-out 參数能够在函数内部改动传进来的值。并传回取代原先的值。因为in-out 參数作为一个变量使用。因此在调用函数时须要在其名字前面须要放一个&标记符来指示该參数是一个函数内部能够改动并传回值的in-out 參数。

   须要注意的是in-out 參数不能没有默认值,可变參数也不能标记为inout參数,也不能使用var或letkeyword来标记它。

func swapTwoInts(inout a:Int,inout b:Int) {

   let temporaryA =a

   a =b

   b =temporaryA

}

       以上swapTwoInts函数定义了两个inout參数,用来在函数内部实现两个參数值的交换。

能够使用例如以下方式调用该函数:

var someInt =3

var anotherInt =107

swapTwoInts(&someInt, &anotherInt)

1.2、 函数类型及使用

    每一个函数都属于一种特定的函数类型,函数类型由參数类型和函数的返回类型组成。

func addTwoInts(a:Int,b:Int) ->Int {

   return a +b

}

func multiplyTwoInts(a:Int,b:Int) ->Int {

   return a *b

}

    比如以上定义的两个函数属于同样的函数类型。其类型为:(Int, Int) -> Int

        在Swift中能够像其他类型一样使用函数类型(函数类型是一种引用类型),如定义一个常量或变量是一个函数类型并为其分配一个适当的函数。

    let anotherMathFunction =addTwoInts

    // anotherMathFunction 被判断为是一个(Int, Int) -> Int函数类型。


         你也可以使用函数类型作为另外函数的參数。例如以下所看到的:

func printMathResult(mathFunction: (Int,Int) ->Int, a:Int, b:Int) {

   println("Result:(mathFunction(a,b))")

}

      printMathResult(addTwoInts,3,5)

    也可以使用函数类型作为另外函数的返回类型,如:

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

}

      // 该函数依据參数的不同值返回不同的函数。 

     var currentValue =3

let moveNearerToZero =chooseStepFunction(currentValue >0)

       currentValue =moveNearerToZero(currentValue)   


  1.3、函数的嵌套

      在Swift中。能够在函数运行体内定义其他函数。被称为函数嵌套,一个函数内部定义的函数称为嵌套函数,最外面定义的函数统称为全局函数。嵌套函数默认对外部隐藏。仅能在定义它的函数内部调用或使用。但包括一个嵌套函数的函数能够返回它所包括的嵌套函数以便嵌套函数能够被外部使用。例如以下所看到的:

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

}

 以上函数chooseStepFunction内部定义了两个内嵌函数。并依据传进的參数值返回两个内嵌函数之中的一个。

二 闭包(Closures)

   2.1 闭包定义

     闭包是一个自包括的功能块,能像函数一样使用,闭包类似于C 和 Objective-C语言中定义的块(blocks)。

     像函数一样闭包也是一种类型(引用类型)。也能分配一个闭包到一个常量或变量,实际上该常量或变量指向该闭包的一个引用。


     全局函数和嵌套函数是闭包的特例。Swift中闭包指的是例如以下三种形式之中的一个:

     1) 全局函数,全局函数不可以捕获不论什么值。

     2) 嵌套函数,嵌套函数能从定义它们的函数中捕获值;

     3) 闭包表达式是没有名字的闭包,是实现闭包的轻量级语法形式,闭包表达式能从它们的使用上下文中捕获值。

     闭包表达式的语法形式为:

{ (parameters) -> return type in

    statements

}

     闭包表达式与函数的主要不同是闭包表达式没有函数名。整个闭包表达式的内容用一对大括号包含,闭包表达式的參数和返回类型也在大括号内部声明,并inkeyword来引出闭包表达式的运行体。

     闭包表达式像函数一样也能使用常量參数、变量參数或inout參数,也能够使用可变參数。但不同的地方是不能给闭包表达式的參数提供默认值。

     例如以下是inline方式使用闭包表达式的一个样例。

     reversed = sort(names, { (s1:String, s2:String) -> Bool in return s1 > s2 } )

2.2  闭包表达式的优化

     当以inline闭包表达式传送一个闭包给一个函数时。闭包表达式的參数和类型能够从函数的參数中加以判断,如

     上面的sorting闭包作为sort函数的第二个參数传递和使用,Swift能从sort函数的第二个參数的类型来判断该闭包的的參数类型和返回类型为(String, String) -> Bool类型。

     因此对于这样的情况,闭包表达式的參数和返回类型在实际代码中非常少须要声明,能够简化为例如以下形式:

      reversed =sort(names, {s1,s2 in return s1 >s2 } )

     Swift中,单表达式的闭包可以隐含返回该表达式的运算结果。因此上面的returnkeyword也可以省略。

     reversed = sort(names, { s1, s2 in s1 > s2 } )

     Swift能够自己主动为inline闭包表达式的參数提供速记名,使用$0,$1,$2等形式来定义和引用闭包表达式包括的參数值。

这时inline闭包表达式的參数列表也能够取消。这时闭包表达式就仅仅包括一个运行体,因此inkeyword也能够省掉。  

         这样上面使用inline闭包表达式的sort函数能够被优化为例如以下最简化形式:

             reversed =sort(names, {$0 >$1 } )

      因为字符串类型实现了一个 >操作符(大于)的字符串操作函数。

该操作函数的函数类型与sort函数的第二个參数须要的函数类型相匹配,因此你能在上面的函数中简单传送一个>操作符。Swift将会帮你判断你想使用它的字符串实现。因此上面的表达式还能够写作例如以下形式:

       reversed =sort(names, >) 

    假设传送一个闭包表达式作为一个函数的最后一个參数。而且闭包表达式太长。

这时还能够使用闭包推后形式。

         闭包推后指的是闭包表达式在调用函数时被写在它支持的函数的參数括号外面。

    因此如上的对sort函数调用时能够写成以下的形式,这样使代码的可读性更强。

    reversed = sort(names) { $0 > $1 }

    假设一个闭包表达式是一个函数的唯一參数,并以闭包推后的形式表达,这时调用函数时函数包括的參数()能够被省去。例如以下样例所看到的:

let strings =numbers.map {

    (var number) ->String in

   var output =""

   while number >0 {

       output =digitNames[number %10]! +output

       number /=10

    }

   return output

}

2.3 值的捕获

        闭包能从定义它们的上下文中捕获引用并赋值给随意的常量或变量。

        闭包能够从它被定义的上下文捕获常量或变量,然后在它的运行体内引用或改动捕获的常量或变量。即使定义常量或变量已经不再有效。

        如嵌套函数能从定义它的外部函数的參数和外部函数中捕获參数、随意常量或变量等。

func makeIncrementor(forIncrement amount:Int) -> () ->Int {

   var runningTotal =0

   func incrementor() ->Int {

       runningTotal +=amount

       return runningTotal

    }

   return incrementor

}

       以上样例。makeIncrementor的内嵌函数incrementor从它的定义函数makeIncrementor中捕获了一个变量和一个參数。

因为捕获的參数在内嵌函数运行体内不被改动,因此以原先值的拷贝形式捕获。而捕获的变量须要在内嵌函数运行体内改动,因此这时内嵌函数捕获的是原先变量的一个引用,捕获一个引用确保在定义内嵌函数的外部函数调用结束后,引用仍可以继续有效,而这些都是由Swift自己主动处理的,用户不须要不论什么操作

    例如以下样例所看到的每次对内嵌函数的调用,引用值在原先的基础上改动。尽管外部函数makeIncrementor调用已经结束。

     let incrementByTen = makeIncrementor(forIncrement:10)

incrementByTen()

// returns a value of 10

incrementByTen()

// returns a value of 20

incrementByTen()

// returns a value of 30


                         版权全部,转载时请清楚注明链接和出处,谢谢!


原文地址:https://www.cnblogs.com/lcchuguo/p/5184216.html