使 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 (未验证)

我是网络开发人员,我接受了该网站的条款和条件

Nim 也具有此功能,如果您对新语言感兴趣

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

回复 作者 jyapayne (未验证)

您在 Nim 中不会获得很多“D 优势”。它因其语法而具有吸引力。

回复 作者 aberba

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

嗨。我是下一代 Shell 的作者。在不知道 D 或 UFCS 是什么的情况下(当时),我只是因为有意义而向 NGS 添加了对 UFCS 的支持,特别是当与多重分派结合使用时。很高兴听到 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()
}

---

下一代 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。

偶数函数也可以是 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 相比,它的语法非常赏心悦目 :)

嗨 Steeve,我经常看到类似的评论。大多数 D 程序员也曾经是/现在也是 C++ 程序员。这也是 D 具有最强大的 C/C++ 代码互操作性的部分原因。

回复 作者 Steeve

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