跳轉到內容

D/D 過渡指南初學者指南

來自華夏公益教科書,開放的世界,開放的書籍

這旨在成為一個針對有經驗的程式設計師的 D 語言快速教程。本文不會花費大量時間解釋 D 語言,而是會演示各種 D 語言功能,並讓它們自己說明問題。如果你正在考慮 D 語言是否適合你,你有兩種一般方法

  • 閱讀大量關於 D 語言的語言比較、文章和口水戰。
  • 看看它是如何工作的。

如果你想採取前者,谷歌 是你的朋友。你可能還想從 Digital Mars 的 D 語言主頁 開始。

如果後者是你的菜,那麼你來了對地方。我們將從小處著手,逐步展示更高階的功能,直到涵蓋語言的所有主要要點。此外,如果你想了解更多關於任何特定主題的示例,DSource 教程專案 是你的正確選擇。

我們將涵蓋的內容

[編輯 | 編輯原始碼]

目前,我們只涵蓋核心語言功能和使用它們所需的最低限度的庫呼叫。你應該意識到的關於 D 語言的第一件事(與 C++ 和 Java 相比)是,它在語言中包含了許多在它的前輩中被降級為庫的功能。因此,雖然 Java 教程如果忽略 java.util.ArrayList 會很疏忽,但在 D 語言中,我們可以使用 int[]。(稍後會詳細介紹。)

本指南的風格

[編輯 | 編輯原始碼]

每節將包含一段程式碼片段,以及描述 D 語言與它的前輩的不同之處。通常,這已經足夠了;有時,還會對與眾不同的功能進行解釋(指的是革命性的變化,而不是演變)。

Hello World

[編輯 | 編輯原始碼]
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);
}
  1. 我們使用 int[],而不是 int ages[] 或者 int* ages 或者 int *ages。陣列在 D 語言中是適當的型別,而不僅僅是指標的別名。熟悉 Perl 和 Python 等指令碼語言的人應該覺得這更舒服。對於你們其他人,等幾分鐘,然後你就會欣賞它。
  2. 陣列字面量。
  3. 請注意,我沒有將其設定為 0。我不應該擔心一些任意資料嗎?不,D 語言將所有變數設定為預設值。
  4. 好吧,除非我透過將其設定為 void 來明確指定。
  5. 我們使用 writefln 來列印格式化的行,很像 C 語言中的 printf。它列印 0 然後是一些任意垃圾。
  6. 這是什麼?陣列有一些附加的屬性,比如 length。它非常方便,尤其是在處理動態陣列(稍後會介紹)時。
  7. for 迴圈完全沒有改變。但是我們不應該在這裡使用 for 迴圈。你將在稍後學習關於 foreach 的內容。
  8. 所有強制轉換都是用 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
}
  1. 字串是字元陣列,不能按元素修改。你將在稍後看到更多關於陣列的資訊。另一個主要說明是 D 語言的字串不是以 null 結尾的。陣列只是跟蹤它們的長度。
  2. firstname 被賦值為 "Walter"。
  3. ~ 是連線運算子。字串中 + 和 ~ 之間沒有歧義。
  4. 一種輸出方式,儘管第 8 種更好
  5. 要修改字串,我們獲得字串的副本(一個副本)。.dup 返回陣列的可變副本。
  6. 由於 b 實際上是指向 a 的指標,所以 b 在這行被修改。firstname 保持不變。
  7. 雖然我們不能按元素修改字串,但我們可以重新賦值給它們。
  8. 由於 fullname 是透過連線建立的,所以它也保持不變。

動態陣列和 Foreach

[編輯 | 編輯原始碼]
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);
}
  1. 定義動態陣列非常直觀;只需省略長度即可。
  2. 擴充套件動態陣列只需調整它的大小即可。
  3. foreach 很棒。這裡有一個簡單的例子:分號之前的部分是來自陣列的變數,分號之後的部分是陣列。沒錯,foreach 可以處理的不僅僅是陣列。
  4. ref 只是表示按引用傳遞。如果沒有它,原始陣列將不受影響。
  5. 內建排序。如果我沒記錯的話,兩種實現都使用快速排序,顧名思義,它非常好。
  6. 現在是另一種形式的 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
}
  1. 為了傳遞引數,我們簡單地讓 main 接收一個字串陣列。與 C/C++ 不同,我們不需要擔心 "int argc",因為陣列知道自己的長度。
  2. 要定義關聯陣列,你只需在方括號中放入一個值型別;否則,它就像一個普通陣列。你可以在裡面放入任何型別(類、陣列等)。但是,如果你使用物件作為鍵,請記住,你將不得不使用雜湊函式和 opCmp 做一些花哨的操作(這超出了本文件的範圍,但在 D 程式語言 - 陣列 中進行了全面描述)。
  3. 這個 "1..$" 結構是陣列的一個切片;它意味著 "返回一個數組,其中包括從索引 1 開始到結尾 ($) 的所有元素"。還有其他方法可以做到這一點,比如 "1..length",但這種方法非常巧妙。如果你想知道為什麼我們要忽略第一個元素,那是因為,就像在 C/C++ 中一樣,第一個元素是程式的名稱。
  4. 為了訪問關聯陣列的元素,你只需為它提供一個鍵型別的索引即可。
  5. 所有關聯陣列都有一個名為 "keys" 的屬性(以及其他屬性,如 values 和 length),它返回所有鍵的陣列。像任何其他陣列一樣,你也可以對該陣列呼叫 sort 屬性。
  6. 這只是另一種列印行的替代方法,每個引數都簡單地轉換為字串並按順序列印。
華夏公益教科書