今天在練習設計模式的時候忽然發現 Swift 裡是沒有棧的——就是 Stack 類。由於我是照著 JAVA 版的設計模式來學習的,所以我只好自己看著實現一個了。
不過說起來,在蘋果官方手冊的「泛型」一章中還真有個棧的栗子,它是用結構體實現的。這也倒是符合了 Swift 一貫的風格——畢竟, Stack 也沒必要去用類實現——對 Swift 裡的結構體來說。
這裡我們簡單回顧一下什麼是棧——其實它在不同的層面可能有不同的定義,這裡我們參考維琪百科的定義:
堆疊(英語:stack),也可直接稱棧。臺灣作堆疊,在電腦科學中,是一種特殊的串列形式的資料結構,它的特殊之處在于只能允許在連結串列或陣列的一端(稱為堆疊頂端指標,英語:top)進行加入資料(英語:push)和輸出資料(英語:pop)的運算。另外堆疊也可以用一維陣列或連結串列的形式來完成。堆疊的另外一個相對的操作方式稱為佇列。
由於堆疊資料結構只允許在一端進行操作,因而按照後進先出(LIFO, Last In First Out)的原理運作。
堆疊資料結構使用兩種基本操作:推入(push)和彈出(pop):
- 推入:將資料放入堆疊的頂端(陣列形式或串列形式),堆疊頂端top指標加一。
- 彈出:將頂端資料資料輸出(回傳),堆疊頂端資料減一。
泛型版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
struct StackG<Element> { private var items = [Element]() mutating func push(item:Element) { items.append(item) } mutating func pop() ->Element? { return items.removeLast() } func empty() -> Bool { return items.isEmpty } func peek() ->Element? { return items.last } } |
這裡我們用一個泛型的陣列來作為棧的儲存,然後就是簡單的 push 和 pop,另外,我還添加了兩個用來檢測堆疊是否為空以及只查看頂端不移除的方法,這樣的話,就和 JAVA 裡的 Stack 類差不多了。
使用的話要按照泛型的方式去聲明: 是 GVersionStack = StackG<SomeClass>()
這裡由於我們使用的是結構體,所以要聲明為變數而不是常量。另外也是因此,我們的部分對結構體內資料有修改的方法都標記了 mutating 關鍵字。
這裡我們看個使用的栗子:
1 2 3 4 5 6 |
class SomeClass { var classNumber = 0 init (number:Int) { classNumber = number } } |
我隨手寫了一個類,一會兒我們用堆疊來存放這個類的實例(的引用)。
1 2 3 4 5 6 7 8 9 10 |
var GVersionStack = StackG<SomeClass>() let a = SomeClass(number: 1) let b = SomeClass(number: 2) let c = SomeClass(number: 3) GVersionStack.push(a) GVersionStack.push(c) GVersionStack.push(b) print(GVersionStack.peek()!.classNumber) |
我聲明瞭三個不同的實例,給它們不同的序號以追蹤,然後壓入棧裡。由於是泛型,所以以後入棧的資料必須是 SomeClass 的類型(或者子類),否則就不能進入。這也符合了 Swift 語言的型別安全風格。
Class 版本
不過,我還是想要讓它實現像 JAVA 那樣的 Stack 類,怎麼辦呢? 我把這個棧改了改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Stack { private var items = [Any]() func push(item:Any) { items.append(item) } func pop() ->Any? { return items.removeLast() } func empty() -> Bool { return items.isEmpty } func peek() ->Any? { return items.last } } |
這一次我使用了類而不再是結構體實現,去掉了泛型所以聲明的時候更方便,也可以聲明成常量——因為它保存的是類的引用。
1 |
let stack = Stack() |
這裡我們把陣列的型別宣告成了 Any ,這樣它就可以指代任何類型了,包括類,也包括 Swift 裡的基本類型。
注意
由於 Swift 裡的基本類型都是經過結構體包裝的,而且它們並不繼承自 AnyObject ,如果像OC那樣寫,就不能放入基本類型啦! 而且,Swift 不會有預設繼承基類的習慣,所以我們使用 Any 而不是 AnyObject 。
不過,這樣也就沒有了類型,不論你放什麼東西進去,Swift 都不能幫你檢查,因為它們都變成了 Any ,這時我們就要手動檢查類型。好了,栗子還是上邊的那個 SomeClass ,這次我們用新的棧來試試:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
let stack = Stack() let a = SomeClass(number: 1) let b = SomeClass(number: 2) let c = SomeClass(number: 3) stack.push(1) //Int stack.push(a) //SomeClass stack.push("abcd") //String while(!stack.empty()) { let tmp = stack.peek() as? SomeClass //Type cast if let a = tmp { print(tmp!.classNumber) } stack.pop() } |
這次我不僅壓入了 SomeClass ,還壓入了字串以及整形數位,一切都是那麼完美。
接下來,我依次彈出堆疊裡的資料,但我在使用它們之前必須先對其進行轉換格式,否則就不能使用——因為 Swift 已經不認識它們了。
延伸閱讀
最後,我把這兩個寫好的堆疊臭不要臉地傳到了自己的 Github上 上,供你下載使用和參考;
關於泛型的內容,參考自這裡;
關於堆疊的維琪百科。
本文由 落格博客 原創撰寫:落格博客 » Swift 裡的 Stack 實現
轉載請保留出處和原文鏈接:https://www.logcg.com/archives/1505.html
註釋