D 语言让我爱不释手的特性

UFCS 赋予你能力去组合可重用的代码,它拥有自然的流程且不牺牲便利性。
80 位读者喜欢这篇文章。
Coding on a computer

早在 2017 年,我就写过一篇关于 为什么 D 编程语言是开发的绝佳选择 的文章。但是 D 语言中有一个突出的特性我没有充分展开讨论:通用函数调用语法 (UFCS)。UFCS 是 D 语言中的一种 语法糖,它允许像调用类型的成员函数一样链式调用任何类型的常规函数(字符串、数字、布尔值等)。

如果你还没有安装 D 语言,安装 D 编译器,以便你可以运行本文中的 D 代码

考虑以下示例代码

// file: ufcs_demo.d

module ufcs_demo;

import std.stdio : writeln;

int[] evenNumbers(int[] numbers)
{
    import std.array : array;
    import std.algorithm : filter;

    return numbers.filter!(n => n % 2 == 0).array;
}

void main()
{
    writeln(evenNumbers([1, 2, 3, 4]));
}

使用你最喜欢的 D 编译器编译这段代码,看看这个简单的示例应用程序的作用

$ dmd ufcs_demo.d
$ ./ufcs_demo
[2, 4]

但是,借助作为 D 语言内置特性的 UFCS,你也可以用自然的方式编写代码

...
writeln([1, 2, 3, 4].evenNumbers());
...

或者完全删除现在变得多余的括号,使其感觉 evenNumbers 像一个属性

...
writeln([1, 2, 3, 4].evenNumbers); // prints 2, 4
...

所以完整的代码现在变成

// file: ufcs_demo.d

module ufcs_demo;

import std.stdio : writeln;

int[] evenNumbers(int[] numbers)
{
    import std.array : array;
    import std.algorithm : filter;

    return numbers.filter!(n => n % 2 == 0).array;
}

void main()
{
    writeln([1, 2, 3, 4].evenNumbers);
}

使用你最喜欢的 D 编译器编译并试用它。正如预期的那样,它产生相同的输出

$ dmd ufcs_demo.d
$ ./ufcs_demo
[2, 4]

在编译期间,编译器自动地将数组作为函数的第一个参数放置。这是一种常见的模式,使得使用 D 语言成为一种乐趣,因为它非常符合你对代码的自然思考方式。结果就是函数式编程风格。

你可能可以猜到这会打印什么

//file: cool.d
import std.stdio : writeln;
import std.uni : asLowerCase, asCapitalized;

void main()
{
    string mySentence = "D IS COOL";
    writeln(mySentence.asLowerCase.asCapitalized);
}

但为了确认一下

$ dmd cool.d
$ ./cool
D is cool

结合 其他 D 语言特性,UFCS 赋予你能力去组合可重用的代码,它拥有自然的流程且不牺牲便利性。

是时候尝试 D 语言了

正如我之前写过的,D 语言是一种非常适合开发的语言。从 D 语言下载页面 可以轻松安装,所以下载编译器,看看示例,亲自体验 D 语言。

接下来阅读什么
标签
User profile image.
Lawrence 是一位自由全栈工程师(React、Node.Js、D)和 100% Linux 用户。他热爱设计和编码。对 SaaS 和现代 Web 充满热情。

29 条评论

甚至在你开始解释 UFCS 之前,你就在使用它了。

`numbers.filter!(n => n % 2 == 0).array`

filter 和 array 都接受一个参数

`array(filter!(n => n % 2 == 0)(numbers))`

你是对的 Jesse。我所做的是提及我之前的帖子,该帖子确实谈到了 filter 函数...它使用了 UFCS 和模板。所以我没有必要解释 filter!... 模板函数和不同的调用约定。

回复 作者 Jesse (未验证)

我是 web 开发者,我已经接受了网站的条款和条件

如果你对新语言感兴趣,Nim 也具有此特性

很高兴知道。我认为这是每种现代语言都应该考虑的东西。特别是当它可以在编译时解决而无需运行时成本时。D 似乎在某些方面启发了 Nim。

回复 作者 jyapayne (未验证)

然后呢?这解决了什么新问题?

嗨。我是 Next Generation Shell 的作者。在不知道 D 或 UFCS 是什么(当时)的情况下,我只是因为感觉有意义而将 UFCS 支持添加到 NGS 中,尤其是当与多重分发结合使用时。很高兴听到 UFCS 功能受到赞扬。

只是为了让你对 NGS 有一些感觉

--- D ---
module ufcs_demo;

import std.stdio : writeln;

int[] evenNumbers(int[] numbers)
{
import std.array : array;
import std.algorithm : filter;

return numbers.filter!(n => n % 2 == 0).array;
}

void main()
{
writeln([1, 2, 3, 4].evenNumbers);
}

--- NGS ---

F even_numbers(numbers:Arr) {
numbers.filter(F(n) n % 2 == 0)
}

F main() {
[1,2,3,4].even_numbers().echo()
}

---

Next Generation Shell 项目位于 https://github.com/ngs-lang/ngs

UFCS 似乎与 .Net 扩展非常相似。它们之间有什么区别?
编译器是否优化 UFCS 指令的输出?

编译器会重写它,使其与你通常的编写方式相同。因此,这对于开发者来说是一种便利。

回复 作者 Rubidium37 (未验证)

我不熟悉 .Net 扩展。但是,对于 D 语言,编译器会像你通常调用它一样重写它。所以这对程序员来说是一种方便的语法。

所以 1.add(2): 在编译期间被重写为 add(2,2)。

回复 作者 Rubidium37 (未验证)

看起来像 Elixir、Elm、Haskell 等中的管道操作符,或者 Clojure 中的箭头宏。

- 我不明白这比编写方法好在哪里
- 如果以后需要添加参数会发生什么?
- 将方法伪装成属性会让人难以理解调用它们的性能影响(读取属性是空操作,调用函数则不是)
- 向标准类型添加属性和方法充其量是奇怪的,最坏的情况是令人困惑的
:/

嘿 Arthur,UFCS 帮助避免编写如下代码:finish(make(bake(1,4)));

在 D 语言中,你可以将其写成:bake(1,2).make(). finish () 或者更符合习惯的写法,1.bake(3).make.finish。因此,它以你在阅读代码时在脑海中处理的顺序读取。

任何其他额外的参数都像 1.call(2,3,4,5,...) 这样编写。当然,只有在特定上下文中使用 UFCS 才有意义。

UFCS 实际上只是一种语法技巧。它不会带来额外的性能成本。D 编译器在编译期间将其重写为正常样式。但对于程序员来说,UFCS 只是美化了你的代码。因此,与使用类相比,它更轻量级。有时人们编写类以获得类似的感觉,但使用 D UFCS 可以给你带来这种体验。UFCS 没有内置到类型中,它只是一种语法糖。

回复 作者 Arthur W. (未验证)

各位好.. 学习 D 编程非常有趣。我的第一门编程语言是 Pascal,然后是 VBA。我曾经认为 D 很无聊,因为我转向了格式化和脚本语言,例如 Rubby n Rays 和 Python。我现在知道 D 与 Pascal 和 .NET 有一些相似之处。我喜欢这种集成。我可以轻松地使用 VBA API 管道。最终会尝试 D。

even numbers 函数也可以是 filter! (...) 的别名。

你甚至不需要下载编译器就可以试用。有一个 https://run.dlang.io/ 可以让你在不安装的情况下试用。

正如你所提到的,它是语法糖。提高了代码可读性 - 使其更接近真实的英语。那么为什么不
writeln.mySentence.asLowerCase.asCapitalized;

或者更进一步接近
print mySentence asLowerCase asCapitalized

很想看到这样的语言。

或者使其成为单行代码

import std.stdio : writeln;
import std.uni : asLowerCase, asCapitalized;

void main()
{
"D IS COOL"
.asLowerCase
.asCapitalized
.writeln;
}

回复 作者 aberba

我会尝试 UFCS,看起来很酷!??

我非常喜欢 D 语言,以及它从 C/C++ 发展而来的感觉,很难相信自发布以来已经 20 年了。这些天它看起来像是 C/C++ 和一点 ruby 的混合体。我希望我能在专业领域中使用它。去年,我不得不清理一个用 C++ 编写的绝对混乱的代码,它泄漏了 GB/s 的内存(不骗你),这本可以是一个用 D 语言重写的很棒的项目。对于像我这样的老 C/C++ 程序员来说,与时下流行的 Rust 相比,它的语法非常顺眼 :)

Creative Commons License本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.