Day6 函式(Functions)

weixin_34249678發表於2017-06-05

本頁包含內容:
- 函式定義與呼叫
- 函式引數與返回值
- 函式引數標籤和引數名稱
- 函式型別
- 巢狀函式

** 1、函式定義與呼叫**
當你定義一個函式時,你可以定義一個或多個有名字和型別的值,作為函式的輸入,稱為引數,也可以定義某種型別的值作為函式執行結束時的輸出,稱為返回型別。每個函式有個函式名,用來描述函式執行的任務。要使用一個函式時,用函式名來“呼叫”這個函式,並傳給它匹配的輸入值(稱作實參)。函式的實參必須與函式參數列裡引數的順序一致。
下面例子中的函式的名字是 greet(person:) ,之所以叫這個名字,是因為這個函式用一個人的名字當做輸入,並返回向這個人問候的語句。為了完成這個任務,你需要定義一個輸入引數——一個叫做 person 的 String 值,和一個包含給這個人問候語的 String 型別的返回值:

<pre>func greet(person:String) -> String {
let greating = "Hello," + person + "!"
return greating;
}</pre>
函式呼叫:
<pre>let msg = greet(person: "Anna")
print(msg)
func greetAgain(person: String) -> String {
return "Hello again, " + person + "!"
}
print(greetAgain(person: "Anna"))
// 列印 "Hello again, Anna!"</pre>

2、 函式引數與返回值

  • 無引數函式
    <pre> func sayHelloWorld() -> String {
    return "hello, world"
    }
    let say = sayHelloWorld()
    print(say)
    // 列印 "hello, world"</pre>
  • 多引數函式(巢狀)
    <pre> func greetting(person:String, alreadyGreated: Bool) -> String {
    if alreadyGreated {
    return greetAgain(person: person)
    }else {
    return greet(person: person)
    }
    }
    let hello = greetting(person: "Tim", alreadyGreated: true)
    print(hello)
    // 列印 " Hello again, Tim! "</pre>
  • 無返回值函式
    func greet1(person: String) {
    print("Hello, (person)!")
    }
    greet1(person: "Dave")
    // 列印 "Hello, Dave!"

    注:因為這個函式不需要返回值,所以這個函式的定義中沒有返回箭頭"->"和返回型別。

    被呼叫時,一個函式的返回值可以被忽略:
    func printAndCount(string: String) -> Int {
    print(string)
    return string.characters.count
    }
    func printWithoutCounting(string: String) {
    let _ = printAndCount(string: string)
    }
    let count = printAndCount(string: "hello, world")
    print(count)
    // 列印 "hello, world" 並且返回值 12
    printWithoutCounting(string: "hello, world")
    // 列印 "hello, world" 但是沒有返回任何值

  • 多重返回值函式
    你可以用元組(tuple)型別讓多個值作為一個複合值從函式中返回。
    下例中定義了一個名為 minMax(array:) 的函式,作用是在一個 Int 型別的陣列中找出最小值與最大值。
    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(array: [8, -6, 2, 109, 3, 71])
    print("min is \(bounds.min) and max is \(bounds.max)")
    // 列印 "min is -6 and max is 109"
    
  • 可選元組返回型別
    // 如果函式返回的元組型別有可能整個元組都“沒有值”,你可以使用可選的( optional ) 元組返回型別反映整個元組可以是nil的事實。你可以通過在元組型別的右括號後放置一個問號來定義一個可選元組,例如 (Int, Int)? 或 (String, Int, Bool)?
    為了安全地處理這個“空陣列”問題,將 minMax(array:) 函式改寫為使用可選元組返回型別,並且當陣列為空時返回 nil :
    <pre>func minAndMax(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)
    }

    if let bounds1 = minAndMax(array: [8, -6, 2, 109, 3, 71]) {
    print("min is (bounds1.min) and max is (bounds1.max)")
    }
    // 列印 "min is -6 and max is 109" </pre>

3、函式引數標籤和引數名稱

每個函式引數都有一個引數標籤( argument label )以及一個引數名稱( parameter name )。
引數標籤在呼叫函式的時候使用,呼叫的時候需要將函式的引數標籤寫在對應的引數前面。
引數名稱在函式的實現中使用。預設情況下,函式引數使用引數名稱來作為它們的引數標籤。

    func someFunction(firstParameterName: Int, secondParameterName: Int) {
        // 在函式體內,firstParameterName 和 secondParameterName 代表引數中的第一個和第二個引數值
    }
    someFunction(firstParameterName: 1, secondParameterName: 2)

所有的引數都必須有一個獨一無二的名字。雖然多個引數擁有同樣的引數標籤是可能的,但是一個唯一的函式標籤能夠使你的程式碼更具可讀性。

  • 指定引數標籤
    你可以在函式名稱前指定它的引數標籤,中間以空格分隔:

    func someFunction1(argumentLabel parameterName: Int) {
        // 在函式體內,parameterName 代表引數值
    }
    

這個版本的 greet(person:) 函式,接收一個人的名字和他的家鄉,並且返回一句問候:

    func greetA(person name: String, from hometown: String) -> String {
        return "Hello \(name)!  Glad you could visit from \(hometown)."
    }
    print(greetA(person: "Bill", from: "Cupertino"))
    // 列印 "Hello Bill! Glad you could visit from Cupertino."

引數標籤的使用能夠讓一個函式在呼叫時更有表達力,更類似自然語言,並且仍保持了函式內部的可讀性以及清 晰的意圖。

  • 忽略引數標籤

如果你不希望為某個引數新增一個標籤,可以使用一個下劃線( _ )來代替一個明確的引數標籤。
<pre>func someFunction2(_ firstParameterName: Int, secondParameterName: Int) {
// 在函式體內,firstParameterName 和 secondParameterName 代表引數中的第一個和第二個引數值
}
someFunction2(1, secondParameterName: 2)</pre>

注: 如果一個引數有一個標籤,那麼在呼叫的時候必須使用標籤來標記這個引數。

  • 預設引數值

你可以在函式體中通過給引數賦值來為任意一個引數定義預設值(Deafult Value)。當預設值被定義後,呼叫這 個函式時可以忽略這個引數。

    func someFunction3(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
        // 如果你在呼叫時候不傳第二個引數,parameterWithDefault 會值為 12 傳入到函式體中。
    }
    someFunction3(parameterWithoutDefault: 3, parameterWithDefault: 6)
    // parameterWithDefault = 6
    someFunction3(parameterWithoutDefault: 4)
    // parameterWithDefault = 12
  • 可變引數
    一個可變引數(variadic parameter)可以接受零個或多個值。
    函式呼叫時,你可以用可變引數來指定函式引數可以被傳入不確定數量的輸入值。
    通過在變數型別名後面加入( ... )的方式來定義可變引數。

可變引數的傳入值在函式體中變為此型別的一個陣列。例如,一個叫做 numbers 的 Double... 型可變引數,在函式體內可以當做一個叫 numbers 的 [Double] 型的陣列常量。
下面的這個函式用來計算一組任意長度數字的算術平均數(arithmetic mean):

    func arithmeticMean(_ numbers: Double...) -> Double {
        var total: Double = 0
        for number in numbers {
            total += number
        }
        return total / Double(numbers.count)
    }
    let res1 =  arithmeticMean(1, 2, 3, 4, 5)
    print(res1)
    // 返回 3.0, 是這 5 個數的平均數。
   let res2 = arithmeticMean(3, 8.25, 18.75)
    print(res2)
    // 返回 10.0, 是這 3 個數的平均數。

注意:一個函式最多隻能擁有一個可變引數。

  • 輸入輸出引數

** 4、 函式型別**

每個函式都有種特定的函式型別,函式的型別由函式的引數型別和返回型別組成。
<pre> func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b
}</pre>
這個例子中定義了兩個簡單的數學函式:addTwoInts 和 multiplyTwoInts。這兩個函式都接受兩個 Int 值, 返回一個 Int 值
這兩個函式的型別是 (Int, Int) -> Int ,可以解讀為“這個函式型別有兩個 Int 型的引數並返回一個 Int 型的值。
下面是另一個例子,一個沒有引數,也沒有返回值的函式:

<pre>func printHelloWorld() {
print("hello, world")
}</pre>
這個函式的型別是: () -> Void ,或者叫“沒有引數,並返回 Void 型別的函式

  • 使用函式型別
    在 Swift 中,使用函式型別就像使用其他型別一樣。例如,你可以定義一個型別為函式的常量或變數,並將適當的函式賦值給它:
    var mathFunction: (Int, Int) -> Int = addTwoInts
    print("Result: (mathFunction(2, 3))")
    // Prints "Result: 5"

有相同匹配型別的不同函式可以被賦值給同一個變數,就像非函式型別的變數一樣:
mathFunction = multiplyTwoInts
print("Result: (mathFunction(2, 3))")
// Prints "Result: 6"

  • 函式型別作為引數型別

      func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
          print("Result: \(mathFunction(a, b))")
      }
      printMathResult(addTwoInts, 3, 5)
      // 列印 "Result: 8"
    

這個例子定義了 printMathResult(::_:) 函式,
它有三個引數:第一個引數叫 mathFunction ,型別是 (Int, Int) -> Int ,你可以傳入任何這種型別的函式;
第二個和第三個引數叫 a 和 b ,它們的型別都是 Int ,這兩個值作為已給出的函式的輸入值。

  • 函式型別作為返回型別

你可以用函式型別作為另一個函式的返回型別。你需要做的是在返回箭頭(->)後寫一個完整的函式型別。

    func stepForward(_ input: Int) -> Int {
        return input + 1
    }
    func stepBackward(_ input: Int) -> Int {
        return input - 1
    }

如下名為 chooseStepFunction(backward:) 的函式,它的返回型別是 (Int) -> Int 型別的函式。
chooseStepF unction(backward:) 根據布林值 backwards 來返回 stepForward(:) 函式或 stepBackward(:) 函式:

    func chooseStepFunction(backward: Bool) -> (Int) -> Int {
        return backward ? stepBackward : stepForward
    }

你現在可以用 chooseStepFunction(backward:) 來獲得兩個函式其中的一個:
var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero 現在指向 stepBackward() 函式。

上面這個例子中計算出從 currentValue 逐漸接近到0是需要向正數走還是向負數走。currentValue 的初始值 是 3 ,這意味著 currentValue > 0 為真(true),這將使得 chooseStepFunction(:) 返回 stepBackwar d(:) 函式。一個指向返回的函式的引用儲存在了 moveNearerToZero 常量中。

    print("Counting to zero:")
    // Counting to zero:
    while currentValue != 0 {
        print("\(currentValue)... ")
        currentValue = moveNearerToZero(currentValue)
    }
    print("zero!")
    // 3...
    // 2...
    // 1...
    // zero!

** 5、巢狀函式**
到目前為止本章中你所見到的所有函式都叫全域性函式(global functions),它們定義在全域性域中。你也可以把函式定義在別的函式體中,稱作巢狀函式(nested functions)。
預設情況下,巢狀函式是對外界不可見的,但是可以被它們的外圍函式(enclosing function)呼叫。一個外圍函式也可以返回它的某一個巢狀函式,使得這個函式可以在其他域中被使用。

你可以用返回巢狀函式的方式重寫 chooseStepFunction(backward:) 函式:

    func chooseStepFunction(backward: Bool) -> (Int) -> Int {
        func stepForward(input: Int) -> Int { return input + 1 }
        func stepBackward(input: Int) -> Int { return input - 1 }
        return backward ? stepBackward : stepForward
    }
    var currentValue = -4
    let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
    // moveNearerToZero now refers to the nested stepForward() function
    while currentValue != 0 {
        print("\(currentValue)... ")
        currentValue = moveNearerToZero(currentValue)
    }
    print("zero!")
    // -4...
    // -3...
    // -2...
    // -1...
    // zero!

相關文章