Swift學習筆記 (十四) 函數(上)

函數是一段完成特定任務的獨立代碼片段。你可以通過給函數命名來標識某個函數的功能,這個名字可以被用來在需要的候“調

用”這個函數來完成它的任務。

Swift 統一的函數語法非常的靈活,可以用來表示任何函數,包括從最簡單的沒有參數名字的 C 風格函數,到複雜的帶局部和外

部參數名的 Objective-C 風格函數。參數可以提供默認值,以簡化函數調用。參數也可以既當做傳入參數,也當做傳出參數,也

就是說,一旦函數執⾏結束,傳入的參數值將被修改。

在 Swift 中,每個函數都有一個由函數的參數值類型和返回值類型組成的類型。你可以把函數類型當做任何其他普通變量類型一

樣處理,這樣就可以更簡單地把函數當做別的函數的參數,也可以從其他函數中返回函數。函數的定義可以寫在其他函數定義

中,這樣可以在嵌套函數範圍內實現功能封裝。

 

函數的定義與調⽤

當你定義一個函數時,你可以定義一個或多個有名字和類型的值,作爲函數的輸入,稱爲參數,也可以定義某種類型的值作爲函

數執⾏結束時的輸出,稱爲返回類型。

每個函數有個函數名,用來描述函數執⾏的任務。要使用一個函數時,⽤函數名來“調用”這個函數,並傳給它匹配的輸入值(稱

作實參)。函數的實參必須與函數參數表⾥參數的順序一致。

下⾯例子中的函數的名字是 greet(person:) ,之所以叫這個名字,是因爲這個函數⽤一個人的名字當做輸入,並返回向這個⼈問

候的語句。爲了完成這個任務,你需要定義一個輸入參數——一個叫做 person 的 String 值,和一個包含給這個人問候語的 

String 類型的返回值:

func greet(person: String) -> String {

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

          return greeting

}

所有的這些信息彙總起來成爲函數的定義,並以 func 作爲前綴。指定函數返回類型時,用返回箭頭 -> (一個連字符後跟一個右

尖括號)後跟返回類型的名稱的方式來表示。

該定義描述了函數的功能,它期望接收什麼作爲參數和執⾏結束時它返回的結果是什麼類型。這樣的定義使得函數可以在別的地

方以⼀種清晰的方式被調用:

print(greet(person: "Anna"))

// 打印“Hello, Anna!”

print(greet(person: "Brian"))

// 打印“Hello, Brian!”

調用 greet(person:) 函數時,在圓括號中傳給它一個 String 類型的實參,例如 greet(person: "Anna") 。正如上面所示,因爲這個

函數返回一個 String 類型的值,所以 greet 可以被包含在 print(_:separator:terminator:) 的調用中,用來輸出這個函數的返回值。

注意

print(_:separator:terminator:) 函數的第一個參數並沒有設置一個標籤,⽽其他的參數因爲已經有了默認值,因此是可選的。

於這些函數語法上的變化詳見下方關於函數參數標籤和參數名以及默認參數值。

在 greet(person:) 的函數體中,先定義了一個新的名爲 greeting 的 String 常量,同時,把對 personName 的問候消息賦值給了 

greeting 。然後用 return 關鍵字把這個問候返回出去。一旦 return greeting 被調用, 該函數結束它的執⾏並返回 greeting 的當

前值。

你可以用不同的輸入值多次調用 greet(person:) 。上⾯的例子展示的是用 "Anna" 和 "Brian" 調用的結果,該函數分別返回了不同

的結果。

爲了簡化這個函數的定義,可以將問候消息的創建和返回寫成一句:

func greetAgain(person: String) -> String {

              return "Hello again, " + person + "!"

}
print(greetAgain(person: "Anna"))

// 打印“Hello again, Anna!”

 

函數參數與返回值

函數參數與返回值在 Swift 中非常的靈活。你可以定義任何類型的函數,包括從只帶一個未命名參數的簡單函數到複雜的帶有表

達性參數名和不同參數選項的複雜函數。

 

無參數函數

函數可以沒有參數。下面這個函數就是一個無參數函數,當被調用時,它返回固定的 String 消息:

func sayHelloWorld() -> String {

             return "hello, world"

}

print(sayHelloWorld())

// 打印“hello, world”

儘管這個函數沒有參數,但是定義中在函數名後還是需要一對圓括號。當被調用時,也需要在函數名後寫一對圓括號。

 

多參數函數

函數可以有多種輸入參數,這些參數被包含在函數的括號之中,以逗號分隔。

下面這個函數用一個人名和是否已經打過招呼作爲輸⼊,並返回對這個⼈的適當問候語:

func greet(person: String, alreadyGreeted: Bool) -> String {

         if alreadyGreeted {

                   return greetAgain(person: person)

         } else {

                   return greet(person: person)

         }

}

print(greet(person: "Tim", alreadyGreeted: true))

// 打印“Hello again, Tim!”

你可以通過在括號內使用逗號分隔來傳遞一個 String 參數值和一個標識爲 alreadyGreeted 的 Bool 值,來調用

greet(person:alreadyGreeted:) 函數。注意這個函數和上面 greet(person:) 是不同的。雖然它們都有着同樣的名字 greet ,但是 

greet(person:alreadyGreeted:) 函數需要兩個參數,⽽ greet(person:) 只需要一個參數。

 

無返回值函數

函數可以沒有返回值。下⾯是 greet(person:) 函數的另一個版本,這個函數直接打印一個 String 值,⽽不是返回它:

func greet(person: String) {

             print("Hello, \(person)!")

}

greet(person: "Dave")

// 打印“Hello, Dave!”

因爲這個函數不需要返回值,所以這個函數的定義中沒有返回箭頭(->)和返回類型。

注意

嚴格地說,即使沒有明確定義返回值,該 greet(Person:) 函數仍然返回一個值。沒有明確定義返回類型的函數返回一個 Void 類

型特殊值,該值爲一個空元組,寫成 ()。

調⽤函數時,可以忽略該函數的返回值:

func printAndCount(string: String) -> Int {

          print(string)

          return string.count

}

func printWithoutCounting(string: String) {

          let _ = printAndCount(string: string)

}

printAndCount(string: "hello, world")

// 打印“hello, world”,並且返回值 12

printWithoutCounting(string: "hello, world")

// 打印“hello, world”,但是沒有返回任何值

第一個函數 printAndCount(string:) ,輸出一個字符串並返回 Int 類型的字符數。第二個函數 printWithoutCounting(string:) 調⽤

了第⼀個函數,但是忽略了它的返回值。當第二個函數被調用時,消息依然會由第一個函數輸出,但是返回值不會被用到。

注意

返回值可以被忽略,但定義了有返回值的函數必須返回一個值,如果在函數定義底部沒有返回任何值,將導致編譯時錯誤。

 

多重返回值函數

你可以用元組(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)

}

minMax(array:) 函數返回一個包含兩個 Int 值的元組,這些值被標記爲 min 和 max ,以便查詢函數的返回值時可以通過名字訪問

它們。在 minMax(array:) 的函數體中,在開始的時候設置兩個工作變量 currentMin 和 currentMax 的值爲數組中的第一個數。然

後函數會遍歷數組中剩餘的值並檢查該值是否比 currentMin 和 currentMax 更⼩或更大。最後數組中的最小值與最大值作爲一個

包含兩個 Int 值的元組返回。

因爲元組的成員值已被命名,因此可以通過 .(點) 語法來檢索找到的最小值與最大值:

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”

需要注意的是,元組的成員不需要在元組從函數中返回時命名,因爲它們的名字已經在函數返回類型中指定了。

 

可選元組返回類型

如果函數返回的元組類型有可能整個元組都“沒有值”,你可以使用可選的元組返回類型反映整個元組可以是 nil 的事實。

你可以通過在元組類型的右括號後放置一個問號來定義一個可選元組,例如 (Int, Int)? 或 (String, Int, Bool)?

注意

可選元組類型如 (Int, Int)? 與元組包含可選類型如 (Int?, Int?) 是不同的。可選的元組類型,整個元組是可選的,而不只是元組中

的每個元素值。

前面的 minMax(array:) 函數返回了一個包含兩個 Int 值的元組。但是函數不會對傳入的數組執⾏任何安全檢查, 如果 array 參數

是一個空數組,如上定義的 minMax(array:) 在試圖訪問 array[0] 時會觸發一個運⾏時錯誤。

爲了安全地處理這個“空數組”問題,將 minMax(array:) 函數改寫爲使用可選元組返回類型,並且當數組爲空時返回nil :

func minMax(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)

}

你可以使用可選綁定來檢查 minMax(array:) 函數返回的是一個存在的元組值還是 nil :

if 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”

 

隱式返回的函數

如果一個函數的整個函數體是一個單行表達式,這個函數可以隱式地返回這個表達式。舉個例子,以下的函數有着同樣的作用:

func greeting(for person: String) -> String {

             "Hello, " + person + "!"

}

print(greeting(for: "Dave"))                           // 打印 "Hello, Dave!"

func anotherGreeting(for person: String) -> String {

            return "Hello, " + person + "!"

}

print(anotherGreeting(for: "Dave"))           // 打印 "Hello, Dave!"

greeting(for:) 函數的完整定義是打招呼內容的返回,這就意味着它能使用隱式返回這樣更簡短的形式。 anothergreeting(for:) 函

數返回同樣的內容,卻因爲 return 關鍵字顯得函數更長。任何一個可以被寫成一 ⾏ return 語句的函數都可以忽略 return 。

正如你將會在 《簡略的 Getter 聲明》 ⾥看到的, 一個屬性的 getter 也可以使用隱式返回的形式。

 

函數參數標籤和參數名稱

每個函數參數都有一個參數標籤(argument label)以及一個參數名稱(parameter name)。參數標籤在調用函數的時候使用;調用

的時候需要將函數的參數標籤寫在對應的參數前面。參數名稱在函數的實現中使用。默認情況下,函數參數使用參數名稱來作爲

它們的參數標籤。

func someFunction(firstParameterName: Int, secondParameterName: Int) {

// 在函數體內,firstParameterName 和 secondParameterName 代表參數中的第一個和第二個參數值

}

someFunction(firstParameterName: 1, secondParameterName: 2)

所有的參數都必須有一個獨一⽆⼆的名字。雖然多個參數擁有同樣的參數標籤是可能的,但是一個唯一的函數標籤能夠使你的代

碼更具可讀性

 

指定參數標籤

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

func someFunction(argumentLabel parameterName: Int) {

               // 在函數體內,argumentLabel 代表參數標籤   parameterName 代表參數名稱

}

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

func greet(person: String, from hometown: String) -> String {

                 return "Hello \(person)! Glad you could visit from \(hometown)."

}

print(greet(person: "Bill", from: "Cupertino"))

// 打印“Hello Bill! Glad you could visit from Cupertino.”

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章