跳转到内容

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. 这只是另一种打印行的替代方法,每个参数都简单地转换为字符串并按顺序打印。
华夏公益教科书