swift 語言的基礎 - 函數 (Function)

基本函數 - 介紹

函數是一個,用以執行特殊目的一個程式區塊這是一個swift的基礎函數說明


func functionName(parameters)-> returnType {
  // function body 
}

舉例來說,有一個做香腸的函數,我們只要把豬肉放進去,裡面怎麼做,因為被函數包起來了,實作細節要進到函數裡詳查,但函數產出的結果,會得到香腸。

func 香腸產生器(放入的肉:豬肉型別)-> 香腸型別 {
  // function body 
  
  return 1 根香腸
}

當然,一個簡單的函數也可沒有input 也沒有output

func greeting() {
    print("Hello World!")
}

如果函數完成了之後,要怎麼樣使用呢?只要在程式的生命週期裡,適當的地方呼叫


greeting()

//或者是

var return = 香腸產生器(5隻豬)

print(return) // 100 根香腸

使用系統內建的函數

其實,print() 就是一個系統內建的函數

print("ABC")

再舉多一個取極大值的例子

import Foundation

var value = max(3, 5)

print(value) //5

在使用系統內建的函數,或是使用別的做好的函數庫,在使用之前,都會有個 import 函數庫的操作。如此,能夠使用已經有的函數庫,就不用自己一再地重覆造輪子了

沒有輪子?自己造一個吧!!!

第一個使用者自行定義的函數

用函數來實作一個加法器

加法器函數範例

func addNumbers(num1: Int, num2: Int) -> Int{
  return num1 + num2
}

有一個函數,名字叫做「addNumbers」,有兩個輸入參數,分別為 num1 與 num2,型別為 Int, 回傳值 是一個 Int, 能夠回傳 兩個數相加的值。

讀懂程式,是在學習寫程式之前很重要的一個步驟

呼叫函數

var result = addNumbers(num1: 3, num2: 4)

print(result). //7

其中 num1 與 num2 是具名參數,如果想要少打一點字,可以使用不具名參數

func addNumbers(_ num1: Int, num2: Int) -> Int{
  return num1 + num2
}

num1 前多了個 “_”

呼叫函數,就要配合改為

var result = addNumbers(5, num2: 6)

print(result). //11

想要為參數加一個別名,也可以寫成醬子

func sum(of num1: Int,and num2: Int) -> Int{
  return num1 + num2
}

num1 前多了個 “of”, num2 前多了個 “and”

呼叫函數,就要配合改為

var result = sum(of :5, and : 6)

print(result). //11

是不是比較容易讀了呢?

函數的多種變化

不定量參數的函數 (Function with variadic parameters)

有時候想要傳很多參數但參數又不是固定幾個的時候,swift 語言提供了一種未限定參數個數的函數,範例如下:


func sum(numbers: Int...) -> Int{
  
  var result = 0
  for num in numbers {
    result += num
  }
  return result
}

型別的後面多了 , 代表著,傳入的參數 numbers, 其實是一個型別為 Int 的 collection,可以讓你傳入 0~很多個參數到函數裡面,而在函數中取值的話,用迴圈把參數裡的值一一取出利用。

呼叫函數的方法為


var rtn = sum(numbers:1,2,3,4,5)
print(rtn)

回傳多個值的函數 (Function with return multiple values)

有時候想要回傳很多值的時候,可以回傳一個 Tuple,範例如下:


func sum(numbers: Int...) -> (String, Int) {
  
  var result = 0
  for num in numbers {
    result += num
  }
  return ("經過計算後的結果", result)
}

巢狀函數 (Neste木Function)

函數裡面也可以再放函數,沒問題的,生命週期也僅限於相同的一個 Code Block 裡使用


func 香腸產生器() {
  // code

  
  func 調味() {
    // code
  }

  調味()
  
}

傳址呼叫 Call By Reference

函數的參數中帶有 inout??


func 肉肉加工函數(肉: 豬肉)-> 豬肉 {
  //bla bla bla

  //肉 = 處理好的肉???
  //如果你想把傳進來的肉再次地賦值,會發現不會成功
  //因為此時傳進來的"肉" 是以 let 存在的變數
  //
  let 處理好的肉 = 肉+肉+肉
  return 處理好的肉
}

//另一種寫法

func 肉肉加工函數(肉: inout 豬肉) { //這裡多 inout
  //肉 就不是 let 型式了, 而是指向記憶體位置的 var
  肉 = 處理好的肉
}

//呼叫方式有點不同

var 將要被處理的肉 : 肉

肉肉加工函數(肉: &將要被處理的肉)  //多了一個&在變數前面

print(將要被處理的肉)   // 其實變成處理好的肉

有關於傳址乎叫 Call ByReference 還是 / 傳值呼叫 Call ByValue

留待下下章 Reference Type / Value Type 時再解釋。

遞迴 Recursion

什麼是遞迴? 原理其實很簡單,就是函數裡的處理過程中,再次呼叫自己的函數。


func recurse() {
  ... ...  
  recurse()
  ...  ...     
}

recurse()

停不下來?? 對,這就是遞迴的重點,要有跳脫的機制,不然就會陷入無窮迴圈,不斷地呼叫,而造成記憶體堆疊而炸掉程式。

經典範例1


func cumulativeSum(_ number:Int) -> Int {
  if number == 1 {  //跳脫條件
    return 1
  } 
  //不符合條件就往下繼續
  return number + cumulativeSum(number - 1)
}


print(cumulativeSum(5))

經典範例2


func fibonacci(_ i:Int) -> Int {
  if i <= 2 { return 1 } 
  return fibonacci(i - 1) + fibonacci(i - 2) 
}


print(fibonacci(5))


函數多載(Overloading)

實務經驗中,函數的功能一樣,但有可能輸入的參數的型別不同,那麼,要讓開發者在使用中儘量地不改變開發的習慣時,就會再多寫一個多載函數


func calAge(_ age:Int){
  ...
}


func calAge(_ age:String){
  ...
}


//呼叫
calAge(18)
calAge("18")

或者,參數的型別相同,但命名的別稱不同,使用的情境也不同。


func calAge(bank age:Int){
  ...
}


func calAge(householdAdmin age:int){
  ...
}


//呼叫
calAge(bank:18)
calAge(householdAdmin:18)

閉包 Closure 也可以算是一種內聯函數(inline function),或是匿名函數的表示方式,但有點篇幅 所以下一章再講

運算子方法(Operator Method)

雖然和函數看起來關係不大,不過也稱得上是隱式的函數的終極簡化版,晚點有機會再說明。

使用函數的好處

  1. 簡化程式與重複利用
  2. 增加程式的可讀性
  3. 每個函數就像是積木一樣,一塊塊排好,就可以做出完整的功能

轉載請註明來源,歡迎對文章中的引用來源進行考證,歡迎指出任何有錯誤或不夠清晰的表達。可以郵件至 [email protected]