D/D 過渡指南初學者指南
外觀
< D 語言入門指南
這旨在成為一個針對有經驗的程式設計師的 D 語言快速教程。本文不會花費大量時間解釋 D 語言,而是會演示各種 D 語言功能,並讓它們自己說明問題。如果你正在考慮 D 語言是否適合你,你有兩種一般方法
- 閱讀大量關於 D 語言的語言比較、文章和口水戰。
- 看看它是如何工作的。
如果你想採取前者,谷歌 是你的朋友。你可能還想從 Digital Mars 的 D 語言主頁 開始。
如果後者是你的菜,那麼你來了對地方。我們將從小處著手,逐步展示更高階的功能,直到涵蓋語言的所有主要要點。此外,如果你想了解更多關於任何特定主題的示例,DSource 教程專案 是你的正確選擇。
目前,我們只涵蓋核心語言功能和使用它們所需的最低限度的庫呼叫。你應該意識到的關於 D 語言的第一件事(與 C++ 和 Java 相比)是,它在語言中包含了許多在它的前輩中被降級為庫的功能。因此,雖然 Java 教程如果忽略 java.util.ArrayList 會很疏忽,但在 D 語言中,我們可以使用 int[]。(稍後會詳細介紹。)
每節將包含一段程式碼片段,以及描述 D 語言與它的前輩的不同之處。通常,這已經足夠了;有時,還會對與眾不同的功能進行解釋(指的是革命性的變化,而不是演變)。
import std.stdio;
void main() {
writeln("Hello World!");
}
- 如你所見,D 語言更像 Java,它使用 import 語句而不是 #include。事實上,D 語言根本沒有預處理器。別擔心;你會看到如何用 D 語言本身來實現幾乎所有預處理器技巧。
- main 可以返回 void。
- std.stdio 和 writefln 是庫的一部分,所以我們在這裡不會詳細討論它們。可以簡單地說,writef 遵循 printf 函式族,writefln 添加了類似 Java 的 "ln" 字尾來表示換行符。
import std.stdio;
void main() {
int[] ages; //1
ages = [15, 23, 14, 29]; //2
int sum; //3
double average = void; //4
writefln("%d %f", sum, average); //5
writefln("We have %d ages", ages.length); //6
for(int i = 0; i < ages.length; ++i) //7
sum += ages[i];
average = (cast(double)sum) / ages.length; //8
writefln("Average age: %.2f", average);
}
- 我們使用 int[],而不是 int ages[] 或者 int* ages 或者 int *ages。陣列在 D 語言中是適當的型別,而不僅僅是指標的別名。熟悉 Perl 和 Python 等指令碼語言的人應該覺得這更舒服。對於你們其他人,等幾分鐘,然後你就會欣賞它。
- 陣列字面量。
- 請注意,我沒有將其設定為 0。我不應該擔心一些任意資料嗎?不,D 語言將所有變數設定為預設值。
- 好吧,除非我透過將其設定為 void 來明確指定。
- 我們使用 writefln 來列印格式化的行,很像 C 語言中的 printf。它列印 0 然後是一些任意垃圾。
- 這是什麼?陣列有一些附加的屬性,比如 length。它非常方便,尤其是在處理動態陣列(稍後會介紹)時。
- for 迴圈完全沒有改變。但是我們不應該在這裡使用 for 迴圈。你將在稍後學習關於 foreach 的內容。
- 所有強制轉換都是用 cast 關鍵字明確指定的。
import std.stdio;
void main() {
string firstname, lastname, fullname; //1
firstname = "Walter"; //2
lastname = "Bright";
fullname = firstname ~ " " ~ lastname; //3
writefln("Congratulations on making a great language " ~ fullname); //4
char[] a = firstname.dup;//5
char[] b = a;
a[0] = 'H'; //6
writefln(b); //prints "Halter"
firstname = "Jacob";//7
writefln("Your name is still %s, right?", fullname); //8, prints Walter Bright
}
- 字串是字元陣列,不能按元素修改。你將在稍後看到更多關於陣列的資訊。另一個主要說明是 D 語言的字串不是以 null 結尾的。陣列只是跟蹤它們的長度。
- firstname 被賦值為 "Walter"。
- ~ 是連線運算子。字串中 + 和 ~ 之間沒有歧義。
- 一種輸出方式,儘管第 8 種更好
- 要修改字串,我們獲得字串的副本(一個副本)。.dup 返回陣列的可變副本。
- 由於 b 實際上是指向 a 的指標,所以 b 在這行被修改。firstname 保持不變。
- 雖然我們不能按元素修改字串,但我們可以重新賦值給它們。
- 由於 fullname 是透過連線建立的,所以它也保持不變。
import std.stdio;
// this is a rather contrived example, please forgive me
void main() {
int[] squares; //1
for(int i = 0; i*i < 273; ++i) { // just some random range
squares.length = i+1; //2
squares[i] = i*i;
}
foreach(ref square; squares) //3, 4
square %= 10; // again, something arbitrary
squares.sort; //5
writefln("Total of %d squares", squares.length);
foreach(index, square; squares) //6
writefln("%d: %d", index+1, square);
}
- 定義動態陣列非常直觀;只需省略長度即可。
- 擴充套件動態陣列只需調整它的大小即可。
- foreach 很棒。這裡有一個簡單的例子:分號之前的部分是來自陣列的變數,分號之後的部分是陣列。沒錯,foreach 可以處理的不僅僅是陣列。
- ref 只是表示按引用傳遞。如果沒有它,原始陣列將不受影響。
- 內建排序。如果我沒記錯的話,兩種實現都使用快速排序,顧名思義,它非常好。
- 現在是另一種形式的 foreach。分號左邊的部分可以指定兩個變數,用逗號隔開。在這種情況下,第一個是索引(想想你典型 for 迴圈中的 "i"),第二個是值。
如果你覺得上面的程式碼缺少了什麼,你是對的:我沒有在 foreach 迴圈中的變數上新增資料型別。D 語言實際上提供了一個 "auto" 關鍵字,它意味著 "自動確定我想要什麼型別",如果缺少型別,它在 foreach 迴圈中被隱式理解。
我知道我在這個例子中把很多重要的東西都放在一起了。本指南的其餘部分將更詳細地闡明所有這些主題,但如果你願意,你也可以隨時閱讀官方語言規範或檢視 dsource 教程。
import std.stdio;
void main(char[][] args) { //1
int[char] charCount; //2
foreach(arg; args[1..$]) //3
foreach(c; arg)
++charCount[c]; //4
foreach(c; charCount.keys.sort) //5
writeln(charCount[c], " copies of character '", c, "'"); //6
}
- 為了傳遞引數,我們簡單地讓 main 接收一個字串陣列。與 C/C++ 不同,我們不需要擔心 "int argc",因為陣列知道自己的長度。
- 要定義關聯陣列,你只需在方括號中放入一個值型別;否則,它就像一個普通陣列。你可以在裡面放入任何型別(類、陣列等)。但是,如果你使用物件作為鍵,請記住,你將不得不使用雜湊函式和 opCmp 做一些花哨的操作(這超出了本文件的範圍,但在 D 程式語言 - 陣列 中進行了全面描述)。
- 這個 "1..$" 結構是陣列的一個切片;它意味著 "返回一個數組,其中包括從索引 1 開始到結尾 ($) 的所有元素"。還有其他方法可以做到這一點,比如 "1..length",但這種方法非常巧妙。如果你想知道為什麼我們要忽略第一個元素,那是因為,就像在 C/C++ 中一樣,第一個元素是程式的名稱。
- 為了訪問關聯陣列的元素,你只需為它提供一個鍵型別的索引即可。
- 所有關聯陣列都有一個名為 "keys" 的屬性(以及其他屬性,如 values 和 length),它返回所有鍵的陣列。像任何其他陣列一樣,你也可以對該陣列呼叫 sort 屬性。
- 這只是另一種列印行的替代方法,每個引數都簡單地轉換為字串並按順序列印。