一個下午讓你掌握Swift基礎 ( 7/9 ) 函數

簡介

這是一個Swift語言教程,基于最新的iOS 9,Xcode 7.3和Swift 2.2,會為你介紹Swift編程非常基礎的內容。從電腦如何工作的全程基本原理到語言結構,你會足夠了解這門語言,來處理數據和管理代碼的行為。

快速鏈接


函數

函數(Functions)是很多編程語言的核心。簡單地說,一個函數讓你定義一個代碼塊來完成指定的任務。然后,無論何時 app 需要執行這個任務,都可以運行這個函數,而不是到處復制和粘貼同樣的代碼。

這篇文章里,你會學習到如何寫自己的函數,以及親眼目睹 Swift 如何讓他們輕松使用。

函數基礎

假設你有一個 app 經常需要打印名字。你可以寫一個函數來實現:

func printMyName() {
    print("我的名字是張嘉夫。")
}

上面的代碼被稱作函數聲明(function declaration)。你用 func 關鍵字來定義一個函數。接著是函數的名字,跟著括號。你會在下個部分學習更多有關這些括號的必要性。

括號之后跟著一個開的花括號,后面是你想在函數里運行的代碼,跟著一個關閉的花括號。函數定義好之后,你可以這樣使用它:

printMyName()

這輸出如下內容:

我的名字是張嘉夫。

如果你懷疑你在之前的文章里已經使用了一個函數,那就對啦!print, 在控制臺里輸出你給它的文本,事實上就是一個函數。

這很好地引向了下個部分,你會在那里學習如何傳遞數據給一個函數,以及獲得返回的數據。

函數參數

在之前的例子里,函數簡單地輸出了一條消息。這很棒,但有的時候你希望參數化(parameterize)你的函數,這可以讓函數進行不同的操作,依據通過它的參數(parameters)傳遞進來的數據。

舉個例子,看下面的函數:

func printMultipleOfFive(multiplier: Int) {
    print("\(multiplier) * 5 = \(multiplier * 5)")
}
printMultipleOfFive(10)

這里,你能看到一個參數的定義,在函數名后面的括號里。它叫做 multiplier,是 Int 類型的。在所有函數里,括含包含被稱作參數列表(parameter list)的東西。

函數會輸出給的任意值的5倍。這個例子里,你可以帶有 10 的值來調用函數,所以函數如下輸出:

10 * 5 = 50

你可以更進一步,讓函數更通用。帶有兩個參數,函數可以輸出任意兩個值的乘積。你可以這樣做:

func printMultipleOf(multiplier: Int, andValue: Int) {
    print("\(multiplier) * \(andValue) = \(multiplier * andValue)")
}
printMultipleOf(4, andValue: 2)

現在函數名字后面的括號里有兩個參數了:一個叫做 multiplier 并且另一個叫做 andValue。兩個都是 Int 類型。

這次,你調用函數的時候傳遞了兩個值。注意第二個參數之前有一個標簽。這個語法是 Swift 的方式,讓你寫的代碼讀起來就像一個句子。上面的例子里,你會像這樣讀最后一行代碼:

Print multiple of 4 and value 2

你可以讓它閱讀起來更清晰,通過給一個參數不同的外置(external)名字。
例如,你可以像這樣改變 andValue 參數的外置名字:

func printMultipleOf(multiplier: Int, and andValue: Int) {
    print("\(multiplier) * \(andValue) = \(multiplier * andValue)")
}
printMultipleOf(4, and: 2)

你分配了一個不同的外置名字,通過把它寫在參數名前面。
這個例子里,andValue 仍然是參數的名字,但調用函數的標簽現在簡寫為 and 了。你可以這樣讀新的調用:

Print multiple of 4 and 2

如果你根本不想要任何外置名字,那可以用下劃線 _,就像你在前面的文章里見到的:

func printMultipleOf(multiplier: Int, _ andValue: Int) {
    print("\(multiplier) * \(andValue) = \(multiplier * andValue)")
}
printMultipleOf(4, 2)

這個例子里,第二個參數沒有外置名字,就像第一個參數。但要明智的使用下劃線。這里,你的表達仍然可以理解,但帶有很多參數的更復雜的函數會變得費解、笨重,如果它沒有外置名字的話。想象一下一個帶有5個參數的函數!

你也可以給參數默認值:

func printMultipleOf(multiplier: Int, and andValue: Int = 1) {
    print("\(multiplier) * \(andValue) = \(multiplier * andValue)")
}
printMultipleOf(4)

區別是第二個參數后面的 =1,意味著如果第二個參數沒有給值,默認就是1。

因此,代碼如下輸出:

4 * 1 = 4

如果你預料大部分時間一個參數都是一個值,那有一個默認值會是很有用的,并且它會簡化你調用函數的代碼。

返回值

你到目前為止看到的所有函數都執行一個簡單的任務,其實也就是,輸出一些東西。函數也能返回一個值。函數的調用者可以把返回值(return value)賦值給一個變量或常量。

這意味著你可以用一個函數來處理數據。通過參數來帶進數據,操作它然后返回它。這是定義一個返回值的函數的方法:

func multiply(number: Int, by byValue: Int) -> Int {
    return number * byValue
}
let result = multiply(4, by: 2)

要聲明一個返回值的函數,在一對括號和打開的花括號中間,添加一個 -> 緊跟著返回值的類型。這個例子里,函數返回一個 Int。

函數里面,使用 return 關鍵詞來返回值。這個例子里,你返回了兩個參數的乘積。

通過使用元祖也可以返回多個值:

func multiplyAndDivide(number: Int, by byValue: Int) -> (multiply: Int,
    divide: Int) {
        return (number * byValue, number / byValue)
}
let result = multiplyAndDivide(4, by: 2)
let multiply = result.multiply
let divide = result.divide

這個函數返回了兩個參數的乘積和商。我們定義了函數返回一個元祖,這個元祖包含兩個 Int 值,并且帶有合適的成員值名稱,實現了這個結果。

通過元祖返回多個值的能力讓使用 Swift 變得很快樂。并且它被證明是一個非常有用的特色,你馬上就會看到。

高級參數管理

傳遞給函數的參數默認是常量,意味著他們不能被修改。舉例說明,看如下代碼:

func incrementAndPrint(value: Int) {
    value += 1
    print(value)
}

這會導致一個錯誤:

 Left side of mutating operator isn't mutable: 'value' is a 'let' constant(修改操作符左側沒法被修改:'value‘ 是一個 'let' 常量)

參數 value 等同于一個用 let 聲明的常量。因此,當函數嘗試遞增它的時候,編譯器拋出了一個錯誤。

要知道 Swift 在值傳遞給函數之前拷貝了它,一個行為叫做 pass-by-value

**注意:**Pass-by-value 和做拷貝是目前為止你在本教程中看到的所有類型的標準行為。你會在后面的教程看到另一種方式,把東西傳遞到函數中,“類”。

通常情況下你想要這個行為。理想情況下,一個函數不會修改它的參數。如果它這么做了,然后你就不能確定參數的值,代碼里可能會出現錯誤的假設,導致了錯誤的數據。

有時候你的確希望讓一個函數直接改變一個參數,一個行為叫做 pass-by-reference。像這樣做:

func incrementAndPrintInOut(inout value: Int) {
    value += 1
    print(value)
}

參數名前的 inout 關鍵字表示這個參數應該使用 pass-by-reference。

現在你需要給函數調用做一個輕微調整:

var value = 5
incrementAndPrintInOut(&value)
print(value)

你在參數前加了個 & 符號,幫助你記住這個參數用的是 pass-by-reference。現在函數可以隨心所欲改變值。

這個例子會如下輸出:

6
6

函數成功遞增了 value,在函數結束后還保留了被修改的數據。這就像參數進入(in)了函數,然后又出來了(out),因此是關鍵詞 inout。

標準庫的例子

Swift 標準庫包含很多準備好的函數供你使用。

你在讀本教程的時候已經看見有一個被使用了:print。它攜帶一個單獨的字符串參數,把它輸出到控制臺。

有一些數學函數值得記憶因為他們經常在手邊使用:

第一個就是 max,帶有兩個值,返回最大值:

let result = max(10, 20)

這個例子里,result 等于 20。

相似的,min 帶有兩個值,返回最小值:

let result = min(10, 20)

這里,result 等于 10。

最后,abs 帶有一個單獨的值然后返回絕對值。例如,-10 的絕對值是 10,10 的絕對值是 10。這是 Swift 函數:

let result = abs(-10)

這次,result 等于 10。

當你繼續使用 Swift 的時候,你會遇到標準庫里的許多其它函數。你不會記住所有的,但重要的是知道存在有很多預定義的函數。如果你覺得可能有一個是為你要做的事準備的,就搜索它。如果 Apple 已經寫了一個被完全測試過的函數,那就沒必要再重復勞動了!

迷你練習

  1. 寫一個叫 printFullName 函數,帶有兩個字符串,叫做 firstName 和 lastName。函數應該輸出全名,定義為 firstName + " " + lastName。用它來輸出你自己的全名。
  2. 改變 printFullName 的聲明,讓它的第二個參數 lastName 沒有外置名字。
  3. 寫一個叫做 calculateFullName 的函數,把全名用字符串(string)返回。用它把你自己的名字保存為一個常量。
  4. 改變 calculateFullName 返回一個元祖,包含全名和名字的長度。你可以用下面的語法來獲得一個字符串的長度:string.characters.count。用這個函數來確定你自己全名的長度。

函數作為變量

這可能會讓人吃驚,但 Swift 里的函數只是簡單的另一種數據類型而已。你可以給它們賦值變量和常量,就像你可以對任意其它值的類型一樣,比如 Int 或 String。

要知道怎么用,看下面的函數:

func add(a: Int, _ b: Int) -> Int {
    return a + b
}

它帶有兩個參數,返回他們的和。

你可以把這個函數賦值給一個變量,像這樣:

var function: (Int, Int) -> Int = add

這里,變量名是 function 并且它的類型是(Int, Int) -> Int。你把 add 函數賦值給這個變量。

然而類型定義很重要。你用函數定義里同樣的方式來定義變量,寫參數列表和返回類型。這里 function 變量是一個函數類型,帶有兩個 Int 參數并且返回一個 Int。

現在你可以用 function 變量,和使用 add 同樣的方式,像這樣:

let result = function(4, 2)

這個例子里,result 等于 6。

現在看看下面的代碼:

func subtract(a: Int, _ b: Int) -> Int {
    return a - b
}

現在,你定義了另一個函數,帶有兩個 Int 參數并且返回一個 Int。你可以設置之前的 function 變量為你的新 subtract 函數,因為 subtract 的參數列表和返回類型和 function 變量的類型是匹配的。

function = subtract
let result = function(4, 2)

這次,result 等于 2。

“可以把函數賦值給變量”會經常在手邊用,是因為它意味著你可以傳遞函數給其他函數。這是它的實際例子:

func printResult(function: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    let result = function(a, b)
    print(result)
}
printResult(add, 4, 2)

printResult 帶有三個參數:

  1. function 是一個函數類型,帶有兩個 Int 參數并且返回一個 Int,像這樣聲明:(Int, Int) -> Int。
  2. a 沒有外置名字,并且類型是 Int。
  3. b 沒有外置名字,并且類型是 Int。
    printResult 調用了傳入的函數,傳給它兩個 Int 參數。
    然后它輸出結果到控制臺:

6

能夠傳遞函數給其它函數是非常實用的,可以幫助你寫可復用代碼。你不止可以傳遞數據來操作,函數作為參數傳遞讓你把代碼如何執行變得更靈活。

關鍵點

  • 使用函數(function)來定義一個任務,想執行多少次都可以,不需要多次寫代碼。
  • 函數可以帶有零或更多參數(parameters),并且可以選擇性返回一個值。
  • 函數的第一個參數在函數調用里沒有標簽(label)。所有隨后的參數用他們的名字作為標簽。
  • 你可以添加一個外置名字(external name)給一個函數參數,來改變函數調用里使用的標簽,或者你可以用一個下劃線來表示沒有標簽。
  • 你可以賦值函數給變量,然后傳遞他們給其它函數。

接下來去哪兒?

函數是組合小的代碼塊到大的單元的第一步。你會繼續這個主題,在下面的文章里學習集合類型(collection types)和結構體(structures)。
在下一篇文章你會學習閉包(closures),和函數相關但更輕量、在“百忙中”也可以輕松聲明。繼續之前,看看前面的挑戰,因為你在理解閉包之前需要理解函數!

挑戰

挑戰 A:黃金時段

當我在熟悉一門編程語言的時候,我做的第一件事就是寫一個函數來確定一個數字是不是素數。這是你的第一個挑戰:
首先,寫下面的函數:

func isNumberDivisible(number: Int, by byNumber: Int) -> Bool

你會用它來判斷一個數字能否被另一個除盡。當 number 可以被 byNumber 除盡的時候應該返回 true。

提示:你可以使用模除(%)操作符來判斷一個數字是不是可以被另一個數字除盡:x % y = 0 的時候 x 可以被 y 除盡。
接下來,寫主函數:

func isPrime(number: Int) -> Bool

如果一個數字是素數(prime)應該返回 true,否則是 false。一個數字如果只能被 1 和自己除盡,那它就是素數。你應該循環數字,從 1 到 這個數字,尋找它的除數。如果它有任何不適 1 和它自己的除數,那這個數字就不是素數。你會需要使用你之前寫的 isNumberDivisible(_:by:) 函數。

使用這個函數來判斷下面的情況:

isPrime(6) // false
isPrime(13) // true
isPrime(8893) // true

提示 1:小于 0 的數字應該被認為不適素數。函數開始的時候檢查這個情況,如果數字小于 0 就早點返回。
提示 2:使用循環來尋找除數。如果從 2 開始到數字自己之前結束,那當你找到一個除數的時候,就可以返回 false。
提示 3:如果你真的想聰明一點,循環可以只從 2 到 number 的平方根,而不是一路直到 number 自己。把它留作一個給你的練習,搞明白這是為什么。想想數字 16,平方根是 4,會很有幫助。16 的除數是 1,2,4,8 和 16。

挑戰 B:遞歸函數

這個挑戰里,我們要看看如果一個函數調用自己會發生什么,一個叫做遞歸(recursion)的行為。這聽起來可能不太尋常,但它可以非常實用。
我們要寫一個函數,計算斐波那契數列(Fibonacci sequence)中的一個值。這個數列中的任意值是前兩個值的和。數列定義前兩個值等于 1。也就是,fibonacci(1) =1 以及 fibonacci(2) = 1。
使用下面的聲明寫這個函數:

func fibonacci(number: Int) -> Int

然后,驗證你寫的函數是否正確,通過用下面的數字執行它:

fibonacci(1)  // = 1
fibonacci(2)  // = 1
fibonacci(3)  // = 2
fibonacci(4)  // = 3
fibonacci(5)  // = 5
fibonacci(10) // = 55

提示 1:對于小于 0 的 number 值,應該返回 0。
提示 2:要開始數列,強行讓 number 等于 1 或 2 的時候返回值 1。
提示 3:對于任何其他值,你需要返回用 number - 1 和 number - 2 來調用 fibonacci 的和。

挑戰源代碼

https://yunpan.cn/cBWff6BiWqPbm (提取碼:f63a)


介紹

歡迎來到Swift世界!Swift是一門蘋果在2014年夏天發布的編程語言。從那之后,Swift發布了一個主要的版本跳躍,成為了開始在蘋果平臺:iOS,OS X,watchOS和tvOS開發的最簡單的方式。

誰適合這篇教程

這篇教程適合懂一點編程、并且希望學習Swift的人。也許你已經為網站寫過一些JavaScript代碼,或者用Python寫過一些簡短的程序。這篇教程就是為你準備的!你會學習到編程的基本概念,同時也會成為Swift語言小能手。

如果你是赤裸裸的編程新手,這篇教程也是為你準備的!教程里貫穿有簡短的鍛煉和挑戰來給你一些編程練習,同時測試你的知識。

需要準備什么

要看這篇教程,你需要準備如下的東西:

  • 一臺運行OS X El Captian(10.11)的Mac,帶有最新發布的更新并且安裝了安全補丁。這樣你才能夠安裝需要的開發工具:最新版本的Xcode。
  • Xcode 7.3 或更新的版本。Xcode是用Swift寫代碼的主要開發工具。最小也需要Xcode 7.3版本,因為那個版本包含Swift 2.2。你可以免費從Mac App Store下載Xcode的最新版本,這里:http://apple.co/1FLn51R

如果你還沒有安裝Xcode最新版本,在繼續看下面的教程前要確定安裝。

如何使用這篇教程

每篇教程都會介紹觸手可及的話題理論,伴隨大量Swift代碼來示范在學習的實際的應用程序。

教程里的所有代碼都是平臺中立的;這意味著不是為iOS、OS X或任何其它平臺而特定。代碼在playgrounds里運行,你在本篇中已經學習了。

在剩下的教程里,你可以把代碼在自己的playground里輸入進去。這樣你就可以和代碼“玩耍”(play around),做一些改變立即就能看見代碼運行的結果。

剩下的教程里會貫穿實際小練習,都是簡短的練習,關于觸手可及的主題。每篇的末尾也有挑戰,會有編程問題也會有長一點的代碼練習來測試你的知識。做完就能掌握大部分的Swift基礎知識。

教程更新

教程會隨Swift語言的更新而更新,會發布在我的簡書和知乎專欄上,搜索“張嘉夫”即可關注我。

目錄

本教程從一些基礎工作開始來讓你起步:

  • 第1篇,編程本質 & Playground基礎 - 這就是,到編程世界的入門介紹!你會從電腦和編程的預覽開始,然后剩余時間給Swift playground打個招呼。
  • 第2篇,變量 & 常量 - 你會學習到變量和常量,這是用來存儲數據的“地方”。你也會學習數據類型以及Swift如何追蹤數據類型并在代碼中進行傳輸。
  • 第3篇,數字類型 & 操作 - 你會從基礎的數字類型比如整形和浮點型數字開始,當然也包括布爾類型。也會看到一些在實際操作,從比較到算數操作如加減。
  • 第4篇,字符串 - 用字符串來存儲文字-從按鈕里的文字到圖片的標注到這篇教程全部的文字,存儲這所有的一切!你會學習到string和character類型,以及基于這些類型的一些常見操作。

在腦海中有基礎數據類型后,就該用那些數據做一些事情了:

  • 第5篇,做判斷 - 代碼不總是直接從頭運行到尾。你會學習在代碼里如何做判決并且設定情況來運行某段代碼。
  • 第6篇,重復步驟 - 繼續不要讓代碼直線運行的主題,你會學習到如何使用循環來重復某些步驟。
  • 第7篇,函數 - 函數是Swift中用來構建代碼的基礎建筑。你會學習到如何定義函數來分組代碼到可復用單元中。
  • 第8篇,閉包(Closures) - 閉包和函數很接近。你會學習到如何使用它們來輕松傳遞代碼塊。

最后一篇會回到數據:

  • 第9節,可選值 - 這篇講可選值,Swift中的一種特殊類型,表示既有可能是一個真實的值也有可能沒有值。這篇的最后你會知道為什么要用可選值以及如何安全地使用它們。

這些基礎會讓你快速開始Swift之路,做好接觸更高級編程主題的準備。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內容