2007-09-25 23:21 墳墓 (Brian Hsu)
這是例用 D 語言的 Template 功能以及動態陣列實作出的簡單的 Stack,沒有什麼實際的用途,只是復習資料結構和測試 Code Snippet 這個 plug-in 的功能而已。不過從這個例子,可以看出來 D 語言比起 C++/Java 是一個相當方便和注重實用的語言,放棄了與 C 完全相容的包袱,但又不像 Java 在設計上把自己綁得太死(例如完全放棄指標),讓 D 可以同時定位在 System/Application Programming 兩塊上。
module stack;
private import tango.io.Stdout;
private import tango.text.convert.Sprint;
// 簡化過的 Template 語法,比 C++/Java 順眼的多
class Stack(Type)
{
Type [] content; // 存放實際資料
// 陣列不應該是負的,這裡面的條件並須在除了執行 member function 的時候外,
// 任何時間點都必須成立。
invariant () {
assert (content.length >= 0);
}
// 不是很好的測試方式,我要再去看看 unit test 的書,
// 軟體工程沒認真上課。orz
unittest {
int value = 0;
// 將 Template 實體化,這個 class 裡的 Type 會全部被代換成
// int 這個資料型態。
//
// 和 C++/Java 不同,注意宣告和實體化的語法有所不同,所以
// 可以一看就知道哪個是宣告,哪個是實體化。
//
// 另外據說這樣子的語法讓 compiler 比較好 parse,也不會有
// 語意上的混淆。
// http://www.digitalmars.com/d/1.0/templates-revisited.html
// 在變數有初始值的時候可以不用宣告型態,編譯器會自動處理,和
// 下一行是完全相同的,可以少打一些字。
auto stack = new Stack!(int);
//Stack!(int) stack = Stack!(int);
stack.push(10);
// 注意可函數沒有參數時,不用加括號,像是 properties 一樣
// 注意 isEmpty/size 都是函式,而不是 data memeber
assert (stack.isEmpty == false);
assert (stack.size == 1);
// value 的內容會被改變,因為 pop 裡第一個參數前加了 out 修飾字
stack.pop(value);
assert (stack.isEmpty == true);
assert (stack.size == 0);
assert (value == 10);
}
// Typesafe Variadic Function
// 內建的變動數目參數列表,可以像陣列一樣存取
public this (Type [] data...) {
content = new Type[data.length];
foreach (int i, Type element; data) {
content[i] = element;
}
}
//! 傳回自己是抄 Tango 的 Stdout 的做法,聽說這又是抄 functional
//! programming 還是 Ruby 的做法(Beyond Java?忘記在哪看到的)。
public Stack!(Type) push (Type element) {
content.length = content.length+1; // 直接把陣列的長度加長
content[content.length-1] = element; // 這樣就多一格可以放資料
return this;
}
//! 不利用指標,但可以達到指標要做的,語法又比 C++ 的 reference 更容易
//! 了解。
//!
//! 取出的資料放在 element 中,注意前面加了一個 out 修飾詞,表示這個變
//! 數是用來當輸出,並且會改變原來的變數內容。
public Stack!(Type) pop (out Type element) {
element = content[content.length-1];
content.length = content.length - 1;
return this;
}
// 轉換成輸出字串用,直接丟到輸出函式時會顯示這個函式回傳的字串
public char [] toUtf8 () {
// 和 C/C++ 類似,array of char 就是字串,不過比 C/C++ 增加
// 很多功能。
char [] result = "\n\n";
auto sprint = new Sprint!(char);
// 從陣列的最後面往前看,和 Stack 先進後出的行為是一樣的
foreach_reverse (Type element; content) {
// 這個和 sprintf 是一樣的,注意 formatting 時我們不用管
// 變數的型態。
//
// PS.這是 Libary 的功能,不是 D 內建的
result ~= sprint.format("|\t{}\t|\n", element);
}
// ~= 代表 array contanction
result ~= "+---------------+\n";
return result;
}
// 這兩個和 Java/C++ 完全一樣
public int size () {return content.length; }
public bool isEmpty() {return (content.length == 0) ? true : false;}
// 雖然有 GC,不過 D 還是可以有解構子
public ~this () {
Stdout.format ("Stack 被消滅了").newline;
}
// Operator Overloading
bool opEquals (Stack!(Type) compare) {
if ( compare.size != this.size )
return false;
foreach (int i, Type value; this.content) {
if (this.content[i] != compare.content[i]) {
return false;
}
}
return true;
}
}
void main ()
{
// 可以替 Data Type 取別名
alias Stack!(int) StackOfInteger;
// typedef 是真的建立一個新的 type
typedef Stack!(int) newStackType;
// 如果宣告成 scope 變數,當 reference 無效時,會自動呼叫解構子
scope stack = new StackOfInteger();
//! Compile Error
//! 這個是不行的,因為兩個被視作完全不同的型態。
//! newStackType newStack = stack;
Stdout (stack.isEmpty).newline;
stack.push(3).push(5).push(6);
// 直接印出 stack 的內容
Stdout (stack);
auto stackCompare1 = stack;
auto stackCompare2 = new StackOfInteger(3,5,6);
Stdout (stackCompare1 == stackCompare2).newline;
stackCompare1.push(7);
Stdout (stackCompare1 == stackCompare2).newline;
// 這裡 stack 的解構子會被呼叫
}
回響