php旅游网站论文,wordpress+4+chm,站长之家seo工具包,松江专业做网站原文 我最近决定在系统编程领域试些小众语言.我已用了Java,Dart和Kotlin等高级语言编程多年了(并试了许多其他相同级别或更高级的语言),需要扩大视野,因为对某些类型应用,这些语言并不是最好的工具.
这篇博文中,我想重点介绍D语言这里,经过一些初步实验,它比其他语…原文 我最近决定在系统编程领域试些小众语言.我已用了Java,Dart和Kotlin等高级语言编程多年了(并试了许多其他相同级别或更高级的语言),需要扩大视野,因为对某些类型应用,这些语言并不是最好的工具.
这篇博文中,我想重点介绍D语言这里,经过一些初步实验,它比其他语言更能引起注意. 我还尝试了Zig和Nim12,但觉得它们不适合我,至少现在是这样. 当然,我已试过Rust,但是Rust虽然在很多方面都是个天才语言,但并没有真正让我对编写代码感到兴奋.相反,一想到周末要花时间与借用检查器作斗争,我就充满了恐惧.
我绝对会在工作环境中使用Rust(且已这样了),因为它的安全保证(不仅是内存安全,还有资源和线程安全)和出色的性能(在低内存消耗和原始速度方面),但对业余爱好项目,谢谢.
在我看来,Nim(另一非常有趣语言),在另一端走得太远了,安全不如速度和快乐重要.因此,如果你喜欢它(速度非常快,创建微小二进制文件且使用很少的内存),它可能只是适合你的语言.
Zig有很多承诺,但目前还没有准备好.尽管它专注于简单性,但也非常冗长且难以正确使用.
D似乎是个很好的平衡.它很容易熟悉,同时有些非常有趣的功能.它已存在了足够长的时间,已足够稳定.
本文,我想分享我所学到的东西,重点是元编程及单元测试.
D简介
在2023年,D并不是一门新语言.它自2001年以来一直存在,但从那时起已有了很大的发展,特别是自2010年左右的D2版本稳定以来.
它有3个不同且维护良好的编译器,下载页:
1,DMD是用D自身编写的参考编译器. 2,GDC是D的GCC前端. 3,基于LLVM的LDC.
DMD一般用来更快编译(事实上,它可能是生产级语言中最快编译器之一),但其他两个一般更擅长优化运行时速度.
在介绍D的功能方面D语言旅游做得非常出色,而D的Gems部分特别有趣,因为它展示了D有的,而大多数其他语言所没有的东西,如(UFCS)统一函数调用语法,域保护,(CTFE)编译时函数求值,(如safe,nogc,mustuse)属性等等.
另见包括消息传递和线本存储的多线程节,用它们来共同支持使用类似Actor模型来编写并发代码. 讨论更高级功能前,先展示一些D示例.
下面显示了D切片的实际效果:
import std.stdio : writeln;
void main()
{int[] test [ 3, 9, 11, 7, 2, 76, 90, 6 ];test.writeln;writeln(First element: , test[0]);writeln(Last element: , test[$ - 1]);writeln(Exclude the first two elements: ,test[2 .. $]);writeln(Slices are views on the memory:);auto test2 test;auto subView test[3 .. $];test[] 1; //将每个元素递增1test.writeln;test2.writeln;subView.writeln;//创建空切片assert(test[2 .. 2].length 0);
}编译并运行它: dmd -ofslices slices.d./slices
[3, 9, 11, 7, 2, 76, 90, 6]
First element: 3
Last element: 6
Exclude the first two elements: [11, 7, 2, 76, 90, 6]
Slices are views on the memory:
[4, 10, 12, 8, 3, 77, 91, 7]
[4, 10, 12, 8, 3, 77, 91, 7]
[8, 3, 77, 91, 7]还可直接dmd -runfile.d或rdmd(DMD自带),从源码运行D程序.甚至可按脚本运行:
#!/usr/bin/env rdmd它显示了许多有趣的特征. 1,test.writeln与writeln(test)相同.这就是UFCS. 2,test[$-1],显示了如何在[]中按数组/切片长度使用$符号. 3,test[2..$],类似同样使用$的Go的典型切片. 4,test[]1,显示了可由编译器优化的向量运算. 5,assert(test[2 .. 2].length 0);,D断定,稍后用来测试单元. 相当不错.
D元编程
D有许多元编程功能.元编程是针对程序自身编程的编程. Lisp可能是使用宏元编程的先驱,但宏并不是元编程的唯一方法. 如,如下例所示,D有允许在编译时检查类型的模板,以特化函数:
safe:
auto concat(T)(T lhs, T rhs) {static if (is(T: double)) {//T可转换为双精return lhs rhs;} else {//~一般是D中的连接符号return lhs ~ rhs;}
}
unittest {assert(2.concat(3) 5);assert(4.2.concat(0.8) 5.0);assert(Hello.concat( D) Hello D);
}运行单元测试: dmd -w -main -unittest -run tests.d //1个模块通过单元测试该示例有点傻,因为D支持重载符号这里,所以只能这样.
如果熟悉Java,concat类似通用静态方法,但与Java不同,D允许编译时检查类型,因此可专门针对某些类型特化函数.
static if是编译时执行的if块,运行时不存在. 注意,模板有两个参数列表:一个包含编译时参数,另一个包含运行时参数.如果D编译器可推导编译时参数,则可省略它. 可用!符号显式提供编译时参数.
如,std.conv标准模块中的to模板,把类型当参数,但因为一般无法推导,因此几乎总是显式传递:
unittest {import std.conv: to;assert(42.to!string 42);
}而这只是最基本的D模板. 还可用template关键字来执行更高级操作,如生成多个函数:
template BiDirectionalConverter(T1, T2) {import std.conv: to;T2 convert(T1 t) {return t.to!T2();}T1 convert(T2 t) {return t.to!T1();}
}
unittest {alias StringIntConv BiDirectionalConverter!(string, int);assert(StringIntConv.convert(20) 20);assert(StringIntConv.convert(20) 20);
}std.conv中的八进制(octal)模板,用来在D中声明编译时的八进制:
void main() {import std.stdio: writeln;import std.conv;writeln(octal!750);
}运行: dmd -run tests.d
488强烈建议浏览D模板教程,以了解更多信息.
D中的另一个模板是插件模板.它是一个允许好像在周围域内编写它一样,直接在调用点粘贴代码的复制和粘贴模板.
mixin template Abcd(T) {T a, b, c, d;
}
unittest {mixin Abcd!int;a 10;assert(a 10);assert(b 0);assert(c 0);assert(d 0);
}最后,还可用串插件这里生成代码串:
///用T类型的a,b和c字段构建一个结构.
string abcStruct(T)(string name) {return struct ~ name~ { ~ T.stringof ~ a; ~ T.stringof ~ b; ~ T.stringof ~ c; ~ }\n;
}
unittest {mixin(abcStruct!string(StringStruct));mixin(abcStruct!int(IntStruct));auto abcstr StringStruct(hey, ho, lets go);assert(abcstr.a hey);assert(abcstr.b ho);assert(abcstr.c lets go);auto abcint IntStruct(42);assert(abcint.a 42);assert(abcint.b 0);assert(abcint.c 0);
}D可用-mixin标志创建包含编译过程中生成的所有插件的文件: dmd -w -main -unittest -mixinmixins.d -run tests.d1个模块通过单元测试现在,查看mixins.d文件,找到D编译器生成的结构:
//测试.d中扩展.d(67)
struct StringStruct { string a; string b; string c; }
//测试.d中扩展.d(68)
struct IntStruct { int a; int b; int c; }或,用pragma编译指示,以便编译时D仅打印生成代码:
pragma(msg, abcStruct!double(DoubleStruct));
//dmd -w -main -oftests tests.d
//结果:
struct DoubleStruct { double a; double b; double c; }更多的mixin技巧 官方D文档中的(Parser)代码生成示例,显示了编译时很容易解析串来生成常量配置数据.
单元测试
前例中,使用unittest块来演示D的一些功能.我想很明显,编译单元中一般不包含这些块中代码,因此编译器运行测试时,必须传递-unittest选项给编译器(要实际运行测试,或执行生成的二进制文件,加上-run选项).
回顾下,如下单元测试:
unittest {assert(2 2 4);
}把上面的4更改为5并运行代码: dmd -w -main -oftests -run tests.d使用-main选项,以便编译器在没有函数时生成空main函数. -w标志,按错误对待警告,-of来命名输出文件.用--help查看所有选项.
如果不打印内容,则所有测试都正常.即没有运行测试. 现在用-unittest重试: dmd -w -main -unittest -run tests.dtests.d(18):[unittest]unittest失败1/1模块失败的单元测试输出非常简单.它只是告诉你有多少模块的测试失败了,及断定失败的文件和行. 对快速测试来说不错,但最好告诉失败的真正原因论坛.
如,这是我想出的一个显示失败断定期望结果和实际结果的小模板,来使断定更强大:
auto assertThat(string desc, string op, T)(T lhs, T rhs) {import std.conv: to;const str assert(lhs ~ op ~ rhs, \ ~desc ~ : \ ~ lhs.to!string() ~ \ ~ op ~ \ ~ rhs.to!string());return mixin(str);
}现在,断定如下:
unittest {assertThat!(adding two and two, )(2 2, 5);
}运行它: dmd -w -main -unittest -run tests.dtests.d-mixin-20(20):[unittest]加二加二:451/1模块失败的单元测试真酷! 顺便,D单元测试一般来验证函数属性是否符合期望(D编译器一般会推导它们,给每个函数手动注解大量属性非常麻烦). 如,在D中实现树时,我试测试:
safe nogc nothrow pure unittest {auto tree Tree([0,0,0,2], [10,11,12,13]);assertThat!(children(2) basic case, )(tree.children(2), [3, -1]);
}仅当按safe nogc nothrow pure注解,推导unittest中使用的函数时,才有效(编译器会传递性检查这些函数). 结果如下: myd dmd -unittest -run source/app.d
...一堆错误略...很有意思!
另一个常见用例是只运行单个测试,编译器不支持,但你可自己做,正如jfondren在D论坛上所示:
module tester1;
unittest { assert(true); }
unittest { assert(!!true); }
unittest { assert(1 ! 1); }
unittest { assert(1 0); }
version (unittest) {bool tester() {import std.meta : AliasSeq;import std.stdio : writef, writeln;alias tests AliasSeq!(__traits(getUnitTests, tester1));static foreach (i; 0 .. tests.length) {writef!Test %d/%d ...(i 1, tests.length);try {tests[i]();writeln(ok);} catch (Throwable t) {writeln(failed);}}return false;}shared static this() {import core.runtime : Runtime;Runtime.moduleUnitTester tester;}
}
void main() {assert(false); //这不会运行
}运行它: dmd -w -main -unittest -run tests.d测试1/4...好测试2/4...好测试3/4...失败测试4/4...好非常整洁,但可能不是你想要的.
这使用了如getUnitTests特征等相当高级的东西(D特征是元编程,如果你来自Rust或Scala,概念可能不一样)和UDA(编译时注解)这里. dub包管理
最后
IDE支持似乎还不错,但与Java,Kotlin,Typescript甚至Rust等主流语言相去甚远. 我首先试使用emacs(你需要获得d模式,然后安装serve-d这里,LSP服务器,也支持VSCode的D支持). 然后注意到D的IntelliJ插件非常强大,并且作为Jebrains产品的大用户,很好惊喜(一般,小众语言在IntelliJ中没有很好的支持)!
向IntelliJ插件的开发者致敬!它提供了非常好的开箱即用体验,来生成片段的漂亮模板,代码浏览(包括进入Dstdlib,非常适合学习),内置文档风格精美,有扫描器,因此在代码中显示警告,通过dfmt自动格式化,内置支持dub. 如果用d-unit作为依赖,甚至可运行测试.
D,还支持CPU和内存分析,及非常好的文档工具ddoc这里,与在Rust中一样,可在编译时执行D文档,确保文档示例总是有效!
我厌倦了像Java这样基于VM的语言,并且不太喜欢编写Rust,D可能会成为我下个最喜欢的语言.