D 编程语言是一种静态类型、通用编程语言,具有类似 C 的语法,可以编译为本机代码。由于许多原因,它非常适合开源软件开发;以下是其中一些原因。
建模能力
您可能会发现自己处于这种情况:您有一个想法,并且想要以您在脑海中思考的方式完全在代码中实现它。但是,有时您必须妥协想法以适应代码,而不是对代码进行建模以适应想法。D 支持多种编程范式,包括函数式风格、命令式、面向对象、元编程和并发(actor 模型),所有这些都和谐地集成在一起。您可以选择任何方便的范式来对代码进行建模,以适应您的想法。
通过使用模板(一种在编译期间生成额外 D 代码并将其编织到其中的功能),您可以将代码描述为编译器生成代码的模式。这对于设计算法而不将其绑定到特定类型尤其有用。借助模板的通用性,平台无关的代码变得容易。通过将模板与条件编译相结合,跨平台应用程序变得更容易实现,并且更有可能收到使用不同操作系统的开发人员的贡献。有了这个,一个程序员可以用更少的代码和有限的时间完成很多工作。
范围,深度集成到 D 中,抽象了容器元素(例如,数组、关联数组、链表等)的访问方式,而不是实际实现。这种抽象使得可以在大量容器类型上设计和使用大量算法,而无需将它们绑定到特定的数据结构。D 的数组切片是范围的实现。最终,您可以用更少的时间编写更少的代码,并降低维护成本。
生产力
开源软件的大多数代码贡献者都在有限的时间内自愿工作。D 让您更有效率,因为您可以在更短的时间内完成更多工作。D 中的模板和范围使程序员能够编写通用且可重用的代码,从而提高工作效率,但这只是 D 在生产力方面优势的几个方面。另一个主要吸引力是 D 的编译速度感觉像 Python、JavaScript、Ruby 和 PHP 等解释型语言,这使得 D 非常适合快速原型设计。
D 可以轻松地与遗留代码接口,从而减轻了移植的需要。它旨在使直接与 C 代码接口变得自然:毕竟,C 是遗留的、编写良好且经过测试的代码、库和底层系统调用(尤其是在 Linux 中)的王者。C++ 代码也在D 中可调用到更大程度。事实上,Python、Objective-C、Lua 和 Fortran 是技术上可在 D 中使用的一些语言,并且有许多第三方努力在这些领域推进 D。这使得大量的开源库可以在 D 代码中使用,这符合开源软件开发的惯例。
可读性和可维护性
import std.stdio; // import standard I/O module
void main()
{
writeln("Hello, World!");
}
D 中的 HelloWorld 演示
任何熟悉类似 C 的编程语言的人都很容易理解 D 代码。此外,D 非常易读,即使是复杂的代码也是如此,这使得错误很容易被发现。可读性对于吸引贡献者也至关重要,这对于开源软件的增长至关重要。
D 中一个简单但非常有用的语法糖是支持使用下划线分隔数字,使它们更具可读性。这对于数学尤其有用
int count = 100_000_000;
double price = 20_220.00 + 10.00;
int number = 0x7FFF_FFFF; // in hexadecimal system
Ddoc 是一种内置工具,可以轻松地从代码注释中自动生成文档,而无需外部工具。文档编写、改进和更新变得不那么具有挑战性,因为它与代码并排进行。
契约是为确保 D 代码完全按照预期运行而设置的检查。就像签署法律合同以确保协议中的每一方都尽其职责一样,D 中的契约式编程确保函数、类等的实现始终产生期望的结果或按预期运行。这种特性对于错误检查非常有用,尤其是在多人协作处理项目的开源软件中。契约可以成为大型项目的救星。D 强大的契约式编程功能是内置的,而不是事后添加的。契约不仅增加了使用 D 的便利性,而且还使编写正确且可维护的代码不再令人头疼。
便捷
协作开发可能具有挑战性,因为代码经常更改并且具有许多移动部件。D 通过支持在作用域内本地导入模块来缓解其中一些问题
// returns even numbers
int[] evenNumbers(int[] numbers)
{
// "filter" and "array" are only accessible locally
import std.algorithm: filter;
import std.array: array;
return numbers.filter!(n => n%2 == 0).array;
}
与 filter 一起使用的 "!" 运算符是模板参数的语法。
上面的函数可以随意传递而不会破坏代码,因为它不依赖于任何全局导入的模块。像这样实现的任何函数都可以在以后增强而不会破坏代码,这对于协作开发来说是一件好事。
通用函数调用语法 (UFCS) 是 D 中的一种语法糖,允许调用常规函数,例如对象的成员函数。函数定义为
void cook(string food, int quantity)
{
import std.stdio: writeln;
writeln(food, " in quantity of ", quantity);
}
它可以像往常一样调用,例如
string food = "rice";
int quantity = 3;
cook(food, quantity);
使用 UFCS,可以像调用成员函数一样调用同一个函数
string food = "rice";
int quantity = 3;
food.cook(quantity);
在编译期间,编译器会自动将 food 放置为函数 cook 的第一个参数。UFCS 使链式常规函数成为可能——让您的代码具有函数式编程的自然感觉。UFCS 在 D 中被大量使用,就像上面 evenNumbers 函数中使用的 filter 和 array 函数的情况一样。将模板、范围、条件编译和 UFCS 结合使用,您可以在不牺牲便利性的情况下获得巨大的力量。
auto 关键字可以用来代替类型。编译器将在编译期间静态推断类型。这使您免于冗长的类型名称,并使编写 D 代码感觉像动态类型语言。
// Nope. Do you?
VeryLongTypeHere variable = new VeryLongTypeHere();
// using auto keyword
auto variable = new VeryLongTypeHere();
auto name = "John Doe";
auto age = 12;
auto letter = 'e';
auto anArray = [1, 2.0, 3, 0, 1.5]; // type of double[]
auto dictionary = ["one": 1, "two": 2, "three": 3]; // type of int[string]
auto cook(string food) {...} // auto for a function return type
D 的 foreach 循环允许循环遍历不同底层数据类型的集合和范围
foreach(name; ["John", "Yaw", "Paul", "Kofi", "Ama"])
{
writeln(name);
}
foreach(number; [1, 2, 3, 4, 4, 6]) {...}
foreach(number; 0..10) {...} // 0..10 is the syntax for number range
class Student {...}
Student[] students = [new Student(), new Student()];
foreach(student; students) {...}
D 中内置的 单元测试 支持不仅消除了对外部工具的需求,而且还使程序员可以方便地在其代码中实现测试。所有测试用例都放在可自定义的 unittest {} 块内
int[] evenNumbers(int[] numbers)
{
import std.algorithm: filter;
import std.array: array;
return numbers.filter!(n => n%2 == 0).array;
}
unittest
{
assert( evenNumbers([1, 2, 3, 4]) == [2, 4] );
}
使用 DMD(D 的参考编译器),您可以通过添加 -unittest 编译器标志将所有测试编译到生成的执行文件中。
Dub 是 D 的内置包管理器和构建工具,可以轻松使用来自 Dub 包注册表的越来越多的第三方包(库)。Dub 负责在编译期间下载、编译和链接这些包,以及升级到未来的版本。
选择
除了提供多种编程范式和功能外,D 还提供其他选择。它目前有三个编译器,全部开源。参考编译器 DMD 自带后端,而另外两个 GDC 和 LDC 分别使用 GCC 和 LLVM 后端。DMD 以其快速的编译速度而闻名,而 LDC 和 GDC 则以生成快速机器代码而闻名,但会牺牲一些编译时间。您可以自由选择适合您用例的任何一个。
D 的某些部分在使用时默认情况下是垃圾回收的。如果您愿意,您也可以选择手动内存管理甚至引用计数。一切都由您选择。
以及更多
D 中有几个我在这篇简短讨论中没有介绍到的语法糖。我强烈建议您查看D 的功能概述、标准库中的隐藏宝藏以及D 的使用领域,以了解更多人们正在使用它做什么。许多组织已经在生产中使用 D。最后,如果您准备开始学习 D,请查看书籍Programming in D。
1 条评论