要解決上一節遇到的搜尋問題,我們需要深入了解Swift語言的陣列(Array)與字串(String)的構造,特別是其「索引(Index)」的部分。
其實上節範例4-3b有一小段程式碼沒有解釋,就是與索引有關,在程式最後一段:
// 範例 4-3b 最後一段
if 分解結果.index(after: 索引) == 分解結果.endIndex {
索引 = 分解結果.startIndex
} else {
索引 = 分解結果.index(after: 索引)
}
這段的作用是讓索引在陣列中循環,如果索引已到達末尾,就返回開頭重新開始,否則就指向下一個索引。範例4-3a也有一段程式碼有同樣目的,但寫法更簡潔:
//範例 4-3a 最後一段
行次 = (行次 + 1) % 行數
4-3a的「行次」相當於4-3b的「索引」,利用除以「行數」所得餘數來控制在陣列中循環,這個方法簡單又方便,為什麼不用呢?這是因為Swift陣列的索引,並不一定總是連續整數,範例4-3b的方法雖然稍麻煩,卻100%通用。以下是進一步說明。
多數情況下,陣列索引的確是以 0 開始的連續整數,以範例4-3b為例,斷句分解後的「分解結果」就是一個這樣的字串陣列,如下圖:
這種情況下,索引是連續整數0, 1, 2, ...,用 索引 = (索引 + 1) % 分解結果.count 恰好可以讓索引在0到122之間循環。但Swift允許陣列的索引不一定是「連續」整數,也不一定從0開始,這個用餘數的方法就無效了,怎麼辦?
Swift所有陣列都會有一個屬性 startIndex 表示起始索引(在上圖為0),還有一個屬性 endIndex 表示索引結束,要注意的是,endIndex 並不是最後一個索引,而是「最後一個索引的下一個值」。例如上圖中,最後一個索引是122,整個陣列的索引範圍是0到122,但是 endIndex 卻是123!
這是Swift語言非常特殊的設計(說不上好壞),也是初學者最容易犯的致命錯誤之一,如果不小心用 endIndex 當作索引取值,會導致 “Index out of range” (索引超出範圍)的錯誤而導致App閃退!
所以,整個陣列的索引範圍,要寫成 startIndex ..< endIndex,也就是說:
分解結果.indices == [分解結果.startIndex ..< 分解結果.endIndex]