Swift是蘋果新推出的程式語言,也是蘋果首個開源語言。相比於原來的Objective-C,Swift要更輕便和靈活。筆者最近使用Swift實踐了大量的演算法(絕大部分是矽谷各大公司的面試題),將心得體會總結於下。此文並不是純粹討論Swift如何實現某一個具體的演算法或者資料結構,如氣泡排序、深度優先遍歷,或是樹和棧,而是總結歸納一些Swift常用的語法和技巧,以便大家在解決面試題中使用。
基本語法
先來看下面一段程式碼
1 2 3 4 5 6 7 |
func swap(chars:[Character], p: Int, q: Int) { var temp = chars[p] chars[p] = chars[q] chars[q] = temp } // Assume array is a character array and it has enough elements swap(array, p: 0, q: 1) |
上面程式碼實現了一個非常簡單的功能,就是交換一個陣列中的兩個值。乍一看非常正確,實際上存在以下幾個問題。
- 在第一個引數前應該加上 inout 關鍵字。因為在Swift中,struct都是按值傳遞,class是按引用傳遞;陣列和字典都是struct。所以要改變原來的chars陣列,在其前部加入inout關鍵字,表示是按引用傳遞。
- p 和 q 之前應該加入下劃線。Swift預設函式的第一個引數是區域性(local)變數,而後續引數都是外部(external)變數,外部變數必須宣告。如果在p和q前加上下劃線,則在呼叫函式時無需宣告外部變數,這樣呼叫起來更簡便。
- temp前的var應該改為let。let用於宣告常量(constant),var用於宣告變數(variable)。swap函式中,temp並未進行修改,所以應當視為常量,用let來宣告。
修改過後的程式碼為
1 2 3 4 5 6 7 |
func swap(inout chars:[Character], _ p: Int, _ q: Int) { let temp = chars[p] chars[p] = chars[q] chars[q] = temp } // Assume array is a character array and it has enough elements swap( |
再來看一段程式碼
1 2 3 4 5 6 |
func toZero(x: Int) -> Int { while x > 0 { x -= 1 } return x } |
這裡在 x -= 1
處會報錯。原因是函式中所有的引數是常量(let),是不可以修改的。解決的方法是在函式中寫上var x = x
,之後就可以對 x 進行修改了
迴圈
Swift迴圈分為for和while兩種,注意他們的結構跟傳統的 Java, C/C++有很大區別,筆者將其總結如下
1 2 3 4 5 |
// Assume names is an array holds enough Strings // for loop for name in names { } for i in 0 ... names.count - 1 { } for i in 0 .. |
以上程式碼非常簡單。需要說明的有兩個,一個是 for _ in 0 .. 。當我們不需要陣列中每一個具體的元素值時,我們就可以用下劃線來代表序號。
另一個是是 repeat { } while i 。這個相當於我們熟悉(java)的
do { } while (i 。
排序
swift排序效率很高,寫法也很簡潔。筆者將其總結如下
1 2 |
// Sort an Int array,ascending nums.sortInPlace({$0 |
活用Guard語句
使用Guard語句可以讓邏輯變得非常清楚,尤其是在處理演算法問題的時候,請看下面的程式碼
1 2 3 4 5 6 7 |
// Returns the index of the first occurrence of needle in haystack, // or -1 if needle is not part of haystack func strStr(haystack: String, _ needle: String) -> Int { var hChars = [Character](haystack.characters) var nChars = [Character](needle.characters) if hChars.count |
上面這段程式碼是求字串needle在字串haystack裡首次出現的位置。我們發現for迴圈中的巢狀非常繁瑣,但是如果使用Guard語句程式碼會變得清晰很多:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
func strStr(haystack: String, _ needle: String) -> Int { var hChars = [Character](haystack.characters) var nChars = [Character](needle.characters) guard hChars.count >= nChars.count else { return -1 } if nChars.count == 0 { return 0 } for i in 0 ... hChars.count - nChars.count { guard hChars[i] == nChars[0] else { continue } for j in 0 .. |
Guard語句的好處是判斷條件永遠是我們希望的條件而不是特殊情況,且成功避免了大量的if巢狀。
另外在上面程式碼中,為何要將字串轉化成陣列進行處理?因為Swift中沒有方法能夠以O(1)的時間複雜度取得字串中的字元,我們常用的string.startIndex.advancedBy(n)
方法,其時間複雜度為O(n)。所以筆者在這裡以空間換時間的方法進行了優化。
總結
Swift是一門獨特的語言,本文對其基本知識和語法進行了適當歸納,文中提到的都是基本細節。下期我們會討論更加進階的內容。