Swift是蘋(píng)果新推出的編程語(yǔ)言,也是蘋(píng)果首個(gè)開(kāi)源語(yǔ)言。相比于原來(lái)的Objective-C,Swift要更輕便和靈活产镐。筆者最近使用Swift實(shí)踐了大量的算法(絕大部分是硅谷各大公司的面試題),將心得體會(huì)總結(jié)于下踢步。此文并不是純粹討論Swift如何實(shí)現(xiàn)某一個(gè)具體的算法或者數(shù)據(jù)結(jié)構(gòu)癣亚,如冒泡排序、深度優(yōu)先遍歷获印,或是樹(shù)和棧述雾,而是總結(jié)歸納一些Swift常用的語(yǔ)法和技巧,以便大家在解決面試題中使用兼丰。
基本語(yǔ)法
先來(lái)看下面一段代碼
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)
上面代碼實(shí)現(xiàn)了一個(gè)非常簡(jiǎn)單的功能绰咽,就是交換一個(gè)數(shù)組中的兩個(gè)值。乍一看非常正確地粪,實(shí)際上存在以下幾個(gè)問(wèn)題。
- 在第一個(gè)參數(shù)的類(lèi)型前應(yīng)該加上 inout關(guān)鍵字琐谤。因?yàn)樵赟wift中蟆技,struct都是按值傳遞,class是按引用傳遞;數(shù)組和字典都是struct质礼。所以要改變?cè)瓉?lái)的chars數(shù)組旺聚,在其前部加入inout關(guān)鍵字,表示是按引用傳遞眶蕉。
- p, q, 和chars 之前應(yīng)該加入下劃線砰粹。Swift 3.0 默認(rèn)函數(shù)的所有數(shù)都是外部(external)變量,外部變量必須聲明造挽。如果在外部變量前加上下劃線碱璃,則在調(diào)用函數(shù)時(shí)無(wú)需聲明外部變量,這樣調(diào)用起來(lái)更簡(jiǎn)便饭入。
- temp前的var應(yīng)該改為let嵌器。let用于聲明常量(constant),var用于聲明變量(variable)谐丢。swap函數(shù)中爽航,temp并未進(jìn)行修改,所以應(yīng)當(dāng)視為常量乾忱,用let來(lái)聲明讥珍。
修改過(guò)后的代碼為
func swap(chars: inout [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(&array, 0, 1)
更進(jìn)一步烧栋,我們可以使用泛型編程傍念,并配合Tuple声诸,在配合上斷言纲爸,就寫(xiě)出了靈活度很高的交換方法嗜桌。代碼如下:
func swap<T>(_ array: inout [T], _ p: Int, _ q: Int) {
assert(p >= 0 && p < array.count)
assert(q >= 0 && q < array.count)
(array[p], array[q]) = (array[q], array[p])
}
再來(lái)看一段代碼
func toZero(x: Int) -> Int {
while x > 0 {
x -= 1
}
return x
}
這里在 x -= 1
處會(huì)報(bào)錯(cuò)恬试。原因是函數(shù)中所有的參數(shù)是常量(let)脚猾,是不可以修改的贸伐。解決的方法是在函數(shù)中寫(xiě)上var x = x
新蟆,之后就可以對(duì) x 進(jìn)行修改了
循環(huán)
Swift循環(huán)分為for和while兩種觅赊,注意他們的結(jié)構(gòu)跟傳統(tǒng)的 Java, C/C++有很大區(qū)別,筆者將其總結(jié)如下
// Assume names is an array holds enough Strings
// for loop
for (index, element) in names.enumerated()
for name in names { }
names.forEach { element in }
for i in 0 ... names.count - 1 { }
for i in 0 ..< names.count { }
for _ in 0 ..< names.count { }
for name in names.reverse() { }
for i in 0.stride(through: names.count - 1, by 2) { }
for i in 0.stride(to: names.count, by: 2) { }
// while loop
var i = 0
while i < names.count { }
repeat { } while i < names.count
以上代碼非常簡(jiǎn)單琼稻。需要說(shuō)明的有兩個(gè)吮螺,一個(gè)是 for _ in 0 ..< names.count { }
。當(dāng)我們不需要數(shù)組中每一個(gè)具體的元素值時(shí)帕翻,我們就可以用下劃線來(lái)代表序號(hào)鸠补。
另一個(gè)是是 repeat { } while i < names.count
。這個(gè)相當(dāng)于我們熟悉(java)的 do { } while (i < names.length)
嘀掸。
注意紫岩,在循環(huán)中,i是let變量睬塌,是不可以修改的泉蝌。
排序
swift排序效率很高歇万,寫(xiě)法也很簡(jiǎn)潔。筆者將其總結(jié)如下
// Sort an Int array勋陪,ascending
nums.sort()
// Sort an Int array without mutating the original one, ascending
var sortedNums = nums.sorted()
// Sort an array with custom-defined objects, ascending
timeIntervals.sort { $0.startTime < $1.startTime }
// Sort keys in a dictionary according to its value, ascending
let keys = Array(dict.keys)
var sortedKeys = keys.sorted {
let value0 = dict[$0]!
let value1 = dict[$1]!
return value0 < value1
}
活用Guard語(yǔ)句
使用Guard語(yǔ)句可以讓邏輯變得非常清楚贪磺,尤其是在處理算法問(wèn)題的時(shí)候,請(qǐng)看下面的代碼
// 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 {
let hCount = haystack.count, nCount = needle.count
if hCount == 0 || hCount < nCount {
return -1
}
for i in 0...nCount - nCount {
if haystack[i] == needle[0] {
for j in 0..<needle.count {
if haystack[i + j] != needle[j] {
break
} else {
if j == needle.count - 1 {
return i
}
}
}
}
}
return -1
}
extension String {
subscript(i: Int) -> Character {
return self[index(startIndex, offsetBy: i)]
}
}
上面這段代碼是求字符串needle在字符串haystack里首次出現(xiàn)的位置诅愚。我們發(fā)現(xiàn)for循環(huán)中的嵌套非常繁瑣寒锚,但是如果使用Guard語(yǔ)句代碼會(huì)變得清晰很多:
func strStr(haystack: String, _ needle: String) -> Int {
guard haystack.count != 0 && haystack.count >= needle.count else {
return -1
}
for i in 0...haystack.count - needle.count {
guard haystack[i] == needle[0] else {
continue
}
for j in 0 ..< needle.count {
guard haystack[i + j] == needle[j] else {
break
}
if j == needle.count - 1 {
return i
}
}
}
return -1
}
Guard語(yǔ)句的好處是判斷條件永遠(yuǎn)是我們希望的條件而不是特殊情況,且成功避免了大量的if嵌套违孝。
另外在上面代碼中刹前,筆者給字符串添加了下標(biāo)實(shí)現(xiàn)(subscript)。這里用到的index(startIndex, offsetBy: i)
方法等浊,其時(shí)間復(fù)雜度為O(n)腮郊。所以如果考慮性能最優(yōu),最好將字符串轉(zhuǎn)化為字符數(shù)組進(jìn)行處理筹燕。
總結(jié)
Swift是一門(mén)獨(dú)特的語(yǔ)言轧飞,本文對(duì)其基本知識(shí)和語(yǔ)法進(jìn)行了適當(dāng)歸納,文中提到的都是基本細(xì)節(jié)撒踪。下期我們會(huì)討論更加進(jìn)階的內(nèi)容过咬。