本文翻译自《learn to tango with D》附件是第一章和第二章的pdf文档,限于D爱好者内部传阅 。
前言
今天的应用程序开发者可以从大量的编程语言中进行选择,但总的说来,大多数开发使用以下三类编程语言之一:
C和c++组,接近于硬件、期望很高的运行性能,而程序员也愿意努力去获得这样较高的执行效率。
Java和c#组,代码编译到一个“托管”的环境。这类语言倾向于简单化,通常有一个可用的预先写成的广阔的代码库。
动态类型的脚本语言,如强势的Perl、Ruby和 Python。这些语言企图快速开发,但运行效率很差。
有数以千计的计算机编程语言存在世上,主流使用的也许有一打。如果有人创造一门新的语言,谁会来看它吗?我们没有任何大公司作后盾,也没有强大的营销团队或数亿美元来推销它。
受侏儒Gimli的话“Certainty of death, small chance of success . . . what are we waiting for?”的启发,我们决心发明和建立一个新的主流编程语言。
我们拥有什么能做的事吗?虽然有人会认为把每种编程语言这个螺母的螺钉拔去,它就留下一个洞。但令人惊讶的是,并没有语言能提供现代的、被证实可用的构建来精确地控制程序的运行,提高产品质量、减少bug。首先想到的是c++,但c++深陷在其身后兼容性的需要里,它甚至要兼容过时的十年前写的代码。这就让我们很难让c++采用一些新想法。其次是编译到虚拟机( VMs)的语言如Java和 C#。
但是这些语言似乎从未能完全摆脱其性能问题,当然,基于虚拟机的语言无法提供接近硬件的能力。
最后,有脚本语言如Perl、Ruby、 Python。这些语言提供了很多的高级编程特征和较高的生产效率,但与c++相比,它们的运行性能很差。他们也遭受着不适合于非常大的项目的怀疑。
通常,编程团队将采取一些混合的方法,他们把Python和c++混合起来,试图得到Python的生产率和C++的高性能。这种方法的频繁使用表明有在编程语言领域存在着一个很大的未满足需求。
D试图填补这一需求。它结合了低级的操作硬件的能力和构建可靠、可维护、可移植的高级代码的最新技术。D已经很好地移植了别的语言已经实现的能力,它支持和整合了多种编程范式象命令式( imperative)、面向对象(OOP)、泛型编程(generic programming)。
那么我们为什么要做呢?在没钱、没背景、没付薪员工、没市场营销的情况下完成这样一个庞大的任务会成功吗?这些表面上不利的条件实际上是优势。D团队没有任何人需要汇报(使用D的用户除外)。我们没有把责任加到投资者身上,他们不懂我们要做什么但想要快速地得到商业回报。D不必一定要适合于公司产品线的某处;它不是定位为亏本处理商品去迁移别的产品。没有市场销售告诉我们我们必须改进D以让它匹配最新流行词du jour(今日特别推荐)。
因为我们都是志愿者,我们对D的工作都是因为我们真正地想要D和对D的热心。对D的工作没有任何clock-punchers(时钟打孔器)或short-timers(短暂计时器)。最后,开源了D语言我们很自由。
D团队还有别的关键性优势。我们中的大部分对C、C++、Java、Perl有十年以上的核心使用经验,并且通晓什么可行,什么不工作。我们编写了大量一次性用后即扔的代码和上百万行大型程序。我们曾经单独工作过,出曾在专业团队中工作过。我们中的大部分活跃在编程社区、教学研讨班和进行技术顾问。
D是我们心甘情愿做的事。这本书是这个工作的一个成果。作者都是D团队的杰出成员。我希望,通过阅读每一行,看到我们把程序员的意图用D优雅的表示出来,你会获得乐趣。
我们创造了D,因为D是我们一直想要的语言。
Walter Bright
第一章 第一步
D编程语言是一种强类型、原生编译计算机语言。如果你熟悉C、C + +或Java,你将觉得与D语言一见如故。D保留了大部分许多程序员用起来已经很舒适的概念,同时又在一些方面产生了一些微妙的和显著的差异,成功地结合若干编程风格,尽力保持一个简洁、适度简单的语法。
D的设计意图是作为一个系统语言,尽管它广泛适用于各种项目。从一个人开发的小程序到大规模的有众多的开发者参与的商业应用程序。在这里,我们本想告诉你所有关于D的优势,但Walter Bright在本书的前言里如此雄辩地陈述,使我们放弃了这个想法。如果你还没有读前言,我们强烈建议你先读一读。
Tango是一个通用的库,提供丰富多样的实用功能。Tango是开源软件,有越来多的跟随者、日益庞大切实可用的兼容的插件。Tango的设计旨在符合有效性和灵活性,反应D语言自身的两个主要特征。应用编程接口(API)提供多种你可能会希望在现代软件开发工具包中找到的服务,如容器、压缩算法、线程、fibers、网络、文字处理。
《 Learn to Tango with D》(学习Tango和D)这本书,是介绍包括D语言和Tango库的。主要特点是呈现出一种浓缩和快节奏的格式,意图是迅速和有效地给你足够的信息。
本章将会给你一个语言基础的概述。它演示了如何编译和执行程序,以及如何安装库和编译器。也论述了模块和包方面的语言模块化开发的问题。后续章节会挖掘语言和库的细节。让我们开始吧!
Hello World
一个简单的输出“hello world”的应用程序是怎么样的呢?复制下面的例子到一个文件命名为hello.d并保存它。
1. import tango.io.Stdout;
2. void main()
3. {
4. Stdout ("hello world").newline;
5. }
这是D语言的一个优势,许多程序员在基本级上是熟悉的。如果你能描述一下这个例子做什么,你会很好地用自己的方式来理解D。让我们来看看相关属性:
第一行用import关键字指出编译器可以通过符号访问控制台输出。该操作在某种意义上相当于C++的 #include和Java的import。
在第二行,用main()定义了程序的入口点。你可以把入口点想成是一个在程序启动时被调用的函数或一个子程序。在D语言中所有的函数必须声明一个返回类型。在这里并没有真正地返回一个值,因此第2行声明返回类型为void(有效,什么东西也没有返回)。
在D语言中,代码块和函数放在一对{ }花括号之间的,类似于其他语言的方式,如c++和Java。
在Tango库中,Stdout代表一种通用的格式化控制台输出,类似于C中的printf()函数或Java中的System.out.print()。第4行通过使用导入的输出模块Stdout向控制台输出欢迎词,并附加一个换行符。
导入符号采用“.”号来标识和定位一个层次结构中的特定功能层次。本例中引入的Stdout属于Tango库中io的子集。分层结构使你可以把各种功能进行模块化组织,Java程序员是比较熟悉的。
D支持模块或分别编译的概念,如这里演示的使用import关键字。在下两节我们还会对module(模块)和package(包)进行深入探讨。
注意:你可能以某种方式配置D编译器。现在,我们假设已经完成了编译器的安装。看本章后面“安装”一节,以了解详情。
要执行本程式,你必须编译它,把它与库支持进行连接,然后运行它。开始的两步通常总是被编译器合并成一步,是个简单的事情。D代码的编译通常用代码编辑器进行管理,一个从属的程序,或者一个集成开发环境(IDE),但是我们会直接从命令行调用编译器编译我们的例子:$ dmd hello.d($表示命令行提示符,下同)
在Windows平台程序的编译结果叫做hello.exe,你可以在命令行输入hello来执行这个程序。
$hello
继续向前,一个或多个参数给命令行程序是相当普遍的,因此让我们修改我们的例子来支持多个问候:
1. import tango.io.Stdout;
2. void main(char[][] args)
3. {
4. if (args.length < 2)
5. Stdout ("usage is: hello name [name] … [name]").newline;
6. else
7. foreach (name; args[1..$])
8. Stdout.formatln ("Hello {}", name);
9. }
我们已经介绍了一些附加的概念,现在开始展示一些语言的核心特征。从上往下,这个例子包含以下内容:
第2行包含一个交互的式样:main(char[][] args)。
第2行使用数组。如果你希望,可以给D程序传递一个字符数组或字符串数组作为命令行参数。
展示了一些数组的属性。在第4行,我们测试了length 属性看命令行参数有没有提供。按照惯例,通过main(char[][] args)提供的第一个参数是程序自身的名字,我们可以忽略它。
数组切片允许一个数组的子集与超集用相同的方式正确地编址。在第7行,我们切掉第一个(习惯的)命令行参数,因为在这里它没有我们需要的值。数组切片在第2章详细介绍。
第7行也介绍了一个foreach遍历一个集合的机制。简短地说,foreach 通过左边的参数揭示右边的参数的元素。本例中遍历的是命令行参数的一个子集,不包括索引为零的元素在内。foreach循环在第2章讨论。
第8行显示了Stdout的一个变化,使用{}指明参数位置以进行格式化输出。该格式化语法与c#中的相似。
编译并运行这个修改后的示例,再加上一些命令行参数:
$ dmd hello.d
$hello Jim Sally
将会有以下输出:
Hello Jim
Hello Sally
现在让我们继续前进到模块编译。我们将使用相同的例子来证实如何把东西拆开,但保留它们间的关系。
源模块
D编程语言的源文件被称为模块,它代表了基本编译单位。当你编译一个D程序时,编译器在模块上操作,并产生一个目标文件(*.obj)代表每个模块。模块一般用文件名去掉扩展名.d进行命名,在模块内部通过module关键字进行识别。例如,如果我们hello.d分裂成两个模块,它可以采取以下的形式:
1. module greetings;
2. import tango.io.Stdout;
3. void hello (char[][] names)
4. {
5. foreach (name; names)
6. Stdout.formatln ("hello {}", name);
7. }
在这里,我们已经把控制台输出功能的代码迁移到另一个独立的模块称为greetings.d,并在第一行用module关键字进行标识。在第3行,我们已经声明一个函数叫做hello,它接受一个字符串数组并把它们中的每个字符串输出到控制台(通过第5和6行)。hello函数不返回任何值。
我们的主模块是print.d,虽然它是修改成下面这个样子:
1. import greetings;
2. void main(char[][] args)
3. {
4. hello (args[1..$]);
5. }
在第1、2行,我们导入greetings模块,以使我们可以访问该模块包含的hello函数。然后,在第4行,我们传递和以前使用这个例子时相同的参数给hello函数。现在编译时需要把两个模块都移交给编译器:
$ dmd hello.d greetings.d
$ hello Jim Sally输出:
Hello Jim
Hello Sally
包、导入及符号可见性
正如前面章节所说,一个单一的源文件被称为一个模块。一个相同目录中相关的各个模块集合组成一个包。模块和包的概念与属于模块中的代码、声明和标识符的可见性相关。换句话说,包和模块确定哪些符号能和不能被程序中的其他模块见到(符号是一种通用的命名程序元素术语,例如函数、常量、枚举、结构、类等等)。
当你导入一个模块,你就可以访问它里边包含的符号。在这里的符号已经隐式或显式地设成public(公有),他们会被别的任意导入模块看到。另一方面,符号标上package、 private或 protected后,每一个另外的导入模块将不能看到它们。以下是如何把这些能见度属性特征施加到模块的符号上的操作:
public:作为public,任何导入本模块的模块都能访问本模块中的所有标识符。
package:只有在同一个目录或者说相同包里的模块才可以访问标有“package”的符号。(如果一个包里有多级目录,那么仅有最内层的包或者说目录里的模块才能访问标有“package”的符号)
protected:符号标明“protected”后,只能被别的模块的派生类(子类)可见,一个类封装了这个属性特征。
private:符号标上private后,只能在本模块内可见,任何外部模块都不能见到此类符号。
为了证实这些用法,下面这个模块揭示了多种不同方面的符号:
1. module MyModule;
2. private int x = 10;
3. package char[] str = "hello";
4. public void myFunction () {}
5. public class MyClass {
6. protected bool yes;
7. }
在第1行,我们声明了一个模块名,它必须匹配文件名(不含文件扩展名)。第2行声明了一个私有整数,它只能在本模块内可见。第3行用package声明了一个字符串,因此它仅能在相同的目录(或者说相同包)里可见。第4行这个public函数,它可以被任何导入本模块的模块见到。
5至7行声明了一个公共的叫做MyClass的类,这个类有一个protected属性特征的布尔型变量。该类本身可被导入本模块的任何模块见到,但声明为protected的布尔型变量yes只能被那些模块中派生自MyClass的类见到。
这些可见性特征是可选的,并且它们是互斥的(不能把它们合并到一起)。如果没有明确指出可见性特征,默认为public。
毫不奇怪,导入语句自身也有可见性并遵循类似于刚才描述的模块符号的规则。如果模块B公共(public)导入模块A,模块C又导入了模块B,那么在B中能访问的A模块的符号在模块C中也同样能访问。相反,如果private导入模块A到模块B中,再导入模块B的任何模块将不能任意地访问模块A中的符号。package和 protected操作与此类推。一个区别是默认的导入可见性实际上是private,以尽量避免属于一个项目中模块的命名空间的污染。第3章你会学到更多高级的使用导入语句的方法。
在随后的章节,你将看到包括一些类似于导入tango.io.Stdout的代码示例。在这些情况下,编译器会期望找到一个在tango/io/目录中叫Stdout.d的模块,这与-I标志有关。为了使你容易使用,大多数D编译器会有一个配置文件,里边会包含一个-I标志。我们将在下一节讨论这个配置文件,也包括编译器的安装。
安装
在本节中,我们来看看如何安装和配置编译器,让它有效运转。
写作本文时,windows和linux下主要的编译器都是用名叫DMD的编译器,(参考http://digitalmars.com/d/index.html)。用DMD前端的GNU工具GDC编译器也是可行的(http://dgcc.sourceforge.net/),它的命令行选项模拟DMD。所有现有的D编译器都把编译和连接结合在一步中。
网络上D出现场合非常多,访问相关网站,你可以获得一个时尚的编译器。不过,为了简化安装,最有效的方法就是从Tango网站http://www.dsource.org/projects/tango/wiki/Download)下载编译器和tango库合二为一的。这个网页有定期更新,捆绑最新的测试编译器和Tango库,这个包一般是一个固实的和稳定的版本。
我们将要安装Windows版本的Tango和DMD(相关的Linux组合包的安装过程是相似的)。首先,访问Tango下载页面http://www.dsource.org/projects/tango/wiki/DmdDownloads并下载适用windows的zip压缩包。把zip压缩包解压到你的硬盘。对于这个安装,你应该保存到一个新目录,让我们用一种叫做/ d的目录。用Windows资源管理器或命令行打开解压后的文件夹,会显示下列目录列表(或类似的列表)。
C:\d
bin
Example
import
lib
1,484 LICENSE
217 README.txt
现在把把/d/bin加到windows path变量中,这样编译器和相关工具就可以正确定位了。例如,从windows命令行输入如下命令:
> set path=\d\bin
设定好路径后,你可以从命令行输入dmd命令,就可以得到编译器的响应。
现在,让我们编译和执行一个例子。先切换到这个目录:
/d/example/console
> cd \d\example\console
接下来,编译和执行一个叫做hello.d的程序
> dmd hello.d
> hello
屏幕显示:
hello, sweetheart
祝贺你,你已经成功安装了Tango和和D !如果你稍加探索,你会发现所有的Tango源代码在/d/import目录中。对Tango来说这是一件很好的事情:所有的源代码是随手可得的。
如果你看看/d/bin,你会找到一个配置文件,称为sc.ini,编译器会在这里查找默认的设置和配置。用一个文本编辑器打开该文件,你应该看到类似以下(简略的)的内容。
DFLAGS="-I%@P%\..\import" -version=Tango -L+tango.lib
这是你刚才安装好DMD后的默认设置和配置。它包含了-I命令指向导入目录,一个版本指示器(-version),一个链接器命令(–L+)指定要链接库tango.lib。如果需要的话,你可以修改本行,或为了特殊的使用情况为编译器提供特定的标志。
注意:每一个具体的编译器可能会有一个不同的可用编译选项集合。
尽一步的,你会看到一个许可文件(license file)和readme.txt文件。请阅读它们,里面都包含重要信息。Tango是多重许可的,遵循学术自由的许可v2.1(Academic Free License v2.1)和一个BSD许可。总的说来,这意味着你可以无障碍地用Tango和D建构软件供你自己使用或作商业用途。
编译工具
你已经看过编译超过一个源模块的例子,但如果你的项目扩展到包括很多模块时怎么办呢?有一些工具可协助完成这个任务,例如Make和Ant。D社区感到这些可以改进的具体方式和想出了一个可供选择的方案,叫做Jake、 Bud 和Rebuild。同时,你可以使用传统的自动化构建工具,值得去考虑一下这些可选方案。
每个工具都是基于一种观念,你可以仅编译一个单一的源模块,其余的依赖部分的编译和链接任务将会被自动添加。例如,我们早期例子hello.d被分裂成两个模块,因而两个模块都必须提供给编译器,以满足链接的需求。代替这个途径的方法,我们可以使用这些工具中的一个,像这样:
jake hello.d
在这种情况下,在hello.d中依赖的模块由hello.d代替我们提供给编译器,就好像我们已经明确地把它们提供给编译器。事实上,你可以让Tango以这样的方式工作。
虽然Jake、 Bud、 和Rebuild 支持一个类似的策略,但他们其实在整个过程中扮演了不同的角色,所以选择一款适合你的任务的工具吧。
Bud:这是一个全功能的建造引擎,生成基于D的可执行文件、库文件和动态链接库(dll)。它有数量众多的选项,是可用工具的首选。支持Windows与Linux平台。更多信息见
http://dsource.org/projects/build
Rebuild:这也是一个全功能的工具,区别是有一个很大的源代码、包管理和安装系统称为D共享软件系统(D Shared Software System (DSSS))。它使用和编译器自身相同的语法解析器,可以运行在不同的平台。更多信息见 http://dsource.org/projects/dsss
Jake:这个工具仅限于Windows,它不能构建库。事实上,它甚至没有任何自身的命令行选项(你使用和编译器本身相同的选项)。使用Jake唯一的好处是有效率。它支持非常快的完整编译,它使得所有模块在同一时间被编译,可以用在较大的项目中。Jake是作为Tango和D在Windows平台捆绑包中的一部分提供的。
我们已经完成了Tango和D的一个概述。认识了D语言的一些关键的语法,介绍了Tango库,展示了Windows环境下的Tango和D的安装。我们还介绍了模块和包的概念,D使用它们来实现软件模块化开发的机制,以及符号的可见性。在下一章中,我们将探究更深层次的语言结构。