學習軌道
/
Swift
Swift
/
練習
/
詩社的第一條規則
詩社的第一條規則

詩社的第一條規則

學習練習

簡介

匯入

Swift 的 Standard Library 提供豐富的功能,而你可以透過匯入外部函式庫和框架,使用更多額外的功能。 這些模組可能直接來自 Swift 專案(例如 Swift Argument Parser)、來自 Apple 平台框架(例如 Foundation 和 Network Framework),或來自第三方開源函式庫(例如 SwiftyBeaver)。

像 Foundation 這類核心模組,也就是 Swift 中最廣泛使用的函式庫之一,會隨 Swift 工具鏈一起提供。 第三方函式庫必須先加入專案設定(例如使用 Swift Package Manager),才能匯入。

匯入模組的方式是寫下import關鍵字,後面接著模組名稱。 所以只要在程式裡加入以下內容,就能匯入 Foundation。

import Foundation

匯入模組後,你的程式碼就能使用它的型別、方法和函式。 例如,匯入 Foundation 會為String增加像components(separatedBy:)這樣實用的工具方法。

按照慣例,import敘述會放在 Swift 檔案的開頭,讓任何閱讀程式碼的人都能馬上看到相依項目。

字串索引

String中的個別字元可以像陣列一樣,使用下標取值的方式來存取。 不過,字串使用的索引並_不是_整數,無法直接操作。 而是必須根據其他已知的索引計算出來,例如startIndex,它指向非空字串中第一個字元的位置,而且要用 String 和 NSString 函式庫提供的方法(匯入Foundation模組後,Swift 的字串就能使用所有 NSString 的屬性和方法)。

例如,假設有下列字串:

let csv = "apple,pear,peach,orange,cherry,lime,goosberry"

你無法寫csv[21]來取得「orange」裡的「g」,必須改為計算一個String.Index型別的值,並以該索引取代。 注意,這些索引本身並不是設計給人直接閱讀的。 它們就是所謂的_不透明索引_,因為人類不需要知道裡面是什麼。

let index = csv.index(csv.startIndex, offsetBy: 21)
csv[index]
// "g"
print(index)
// prints Index(_rawBits: 1376513)

不過要注意,如果偏移量不是有效的索引,也就是說,如果算出來的索引在startIndex之前或endIndex之後,這個操作就會引發錯誤,導致程式崩潰。 為了避免這個問題,你可以指定一個限制索引。 這會回傳一個可選的索引,對於原本無效的索引則會回傳 nil。

let tooFar = csv.index(csv.startIndex, offsetBy: 200)
// error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
let tooFarButSafe = csv.index(csv.startIndex, offsetBy: 200, limitedBy: csv.endIndex)
// nil

此外,索引不能在不同字串之間共用。 例如,用上面算出的「orange」中「g」的index去索引字串fam = "This is my family: 👨‍👩‍👦‍👦, this is our dog: 🐶, this is our cat: 🐱!",也就是fam[index],會讓程式崩潰。

這一切背後有很多原因,但基本上都歸結為一句話:「Unicode 很棘手」。

一般來說,如果你需要隨機存取個別字元,你大概應該改用其他資料結構,例如Array<Character>。

一些實用的方法

Swift 有first和last兩個方法,可以取得字串的第一個和最後一個字元。 這些方法會回傳可選的字元,所以如果字串是空的,就會回傳 nil。

let str = "Hello, world!"
print(str.first) // Prints Optional("H")
print(str.last)  // Prints Optional("!")

你也可以用prefix和suffix方法取得字串的子字串。 這些方法接受一個整數作為引數,並回傳包含字串前 n 個或後 n 個字元的子字串。

let str = "Hello, world!"
print(str.prefix(5)) // Prints "Hello"
print(str.suffix(6)) // Prints "world!"

字串方法

處理文字是程式設計中常見的工作。 Swift 與 Foundation 框架提供了許多方便的方法和屬性,用來檢視、轉換和操作字串。

使用components(separatedBy:)分割字串

要根據分隔符號把字串分割成子字串陣列,請使用components(separatedBy:)(需要匯入 Foundation):

import Foundation

let sentence = "Hello, world!"
let words = sentence.components(separatedBy: " ")
print(words) // Prints ["Hello,", "world!"]

使用hasPrefix(_:)和hasSuffix(_:)檢查前綴和後綴

要檢查字串是否以特定的字元序列開頭或結尾,請使用hasPrefix(_:)和hasSuffix(_:):

let greeting = "Hello, world!"
print(greeting.hasPrefix("Hello"))  // Prints true
print(greeting.hasSuffix("world!")) // Prints true

改變大小寫

你可以用這些標準方法和屬性來轉換字串的大小寫:

  • lowercased():回傳字串的小寫副本。
  • uppercased():回傳字串的大寫副本。
  • capitalized:回傳每個單字首字母大寫的副本(來自 Foundation)。
let greeting = "hello, world!"
print(greeting.lowercased())  // Prints "hello, world!"
print(greeting.uppercased())  // Prints "HELLO, WORLD!"
print(greeting.capitalized)   // Prints "Hello, World!"

使用replacingOccurrences(of:with:)取代子字串

要用另一個字串取代目標子字串的所有出現處,請使用replacingOccurrences(of:with:)(來自 Foundation):

let greeting = "Hello, world!"
let updated = greeting.replacingOccurrences(of: "world", with: "Swift")
print(updated) // Prints "Hello, Swift!"

使用joined(separator:)連接字串

joined(separator:)方法屬於字串的集合,例如[String]。 它會用分隔符號把元素連接成單一字串:

let words = ["Hello", "world"]
let sentence = words.joined(separator: ", ")
print(sentence) // Prints "Hello, world"

說明

鎮上新開了一間詩社,你正考慮參加。 因為過去發生過一些狀況,這間詩社有著一套非常特別的入場規定,你得在嘗試進門之前先摸清楚。

詩社有兩扇門,兩扇都有人把守。 為了進場,你得先推理出當天的密碼:

1. 將字串拆分成個別的行

你首先要做的,是把這首詩拆成個別的行。 警衛會朗誦一首詩,而你得把它拆成一行一行。

實作splitOnNewlines函式,它接受一個字串,並以換行字元為分隔符號,將它拆成字串陣列。 這個函式應該回傳一個字串陣列,其中每個字串都是這首詩的其中一行。

splitOnNewlines("Hello.\nHow are you?\n\nI'm doing fine.")
// returns ["Hello." ,"How are you?", "", "I'm doing fine"].

2. 前門

  1. 警衛會朗誦一首詩
  2. 你得把這首詩拆成個別的行。
  3. 你得取出每一行的第一個字母。
    • 如果該行是空的,你應該改用_代替。
  4. 你得把這些字母組合起來,它們會形成一個單字。

舉例來說,他們最喜歡的作家之一是 Michael Lockwood,他寫了下面這首_藏頭詩_,意思是每個句子的第一個字母會組成一個單字:

Stands so high
Huge hooves too
Impatiently waits for
Reins and harness
Eager to leave

當警衛朗誦這首詩時,你會把它拆成個別的行,並以每一行的第一個字母作答,也就是["S", "H", "I", "R", "E"]。

接著,警衛會把你回覆的字母陣列所組成的單字交給你,要你把它變成首字母大寫的形式。 最後你回傳的密碼是"SHIRE",就能進去了。

實作frontDoorPassword函式,它接受一個代表這首詩的字串。 這個函式應該回傳你會告訴警衛的密碼。

frontDoorPassword("Stands so high\nHuge hooves too\nImpatiently waits for\nReins and harness\nEager to leave")
// returns "SHIRE"

3. 後門

在詩社的後方,你會遇到最有名的詩人,那裡就像是貴賓區。 因為這不是人人都能進的,後門的流程稍微複雜一點。

  1. 警衛會朗誦一首詩。
  2. 你得把這首詩拆成一行一行。
  3. 你得取出每一行的最後一個字母。
    • 行尾有時會有空白,所以你需要把它們去掉。
  4. 你得把這些字母組合起來,然後在這些字母組成的單字後面加上", please"。

舉例來說,前面提到的那首詩同時也是_藏尾詩_,意思是每個句子的最後一個字母會組成一個單字:

Stands so high
Huge hooves too
Impatiently waits for
Reins and harness
Eager to leave

當警衛朗誦這首詩時,你會把它拆成個別的行、去掉行尾的空白,並以每一行的第一個字母作答,也就是["h", "o", "r", "s", "e"]。

接著,警衛會把你回覆的字母陣列所組成的單字交給你,要你把它變成首字母大寫的形式,並在後面加上", please"。 最後你回傳的密碼是"horse, please",就能進去了。

實作backDoorPassword函式,它接受一個代表這首詩的字串。 這個函式應該回傳你會告訴警衛的密碼。

backDoorPassword("Stands so high\nHuge hooves too\nImpatiently waits for\nReins and harness\nEager to leave")
// returns "horse, please"

4. 密室

詩社的後室裡還有另一扇門,通往只有最頂尖的詩人才能進入的密室。

  1. 警衛會朗誦一首詩。
  2. 你得把這首詩拆成一行一行。
  3. 你得取出第 ith 行的第 ith 個字母。
  4. 你得把這些字母組合起來,然後在這些字母組成的單字後面加上"!"。
  5. 你得把這個單字變成全大寫的形式。

舉例來說,前面提到的那首詩經過修改後,就符合這個模式:

Stands so high
Huge hooves too
Impatiently waits for
Rider with harness
Eager to leave

當警衛朗誦這首詩時,你會把它拆成個別的行、去掉行尾的空白,並以每一行的第 ith 個字母作答,也就是 ["S", "u", "p", "e", "r"]。

接著,警衛會把你回覆的字母陣列所組成的單字交給你,要你把它變成全大寫的形式,並在後面加上"!"。 最後你回傳的密碼是SUPER!,就能進去了。

實作secretRoomPassword函式,它接受一個代表這首詩的字串。 這個函式應該回傳你會告訴警衛的密碼。

secretRoomPassword("Stands so high\nHuge hooves too\nImpatiently waits for\nRider with harness\nEager to leave")
// returns "SUPER!"
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Swift Exercism

準備好開始 詩社的第一條規則 了嗎?

註冊 Exercism,透過 35 個概念116 個練習 和真人引導來學習並精通 Swift,全部免費。