使 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]

但是,由于 UFCS 是 D 语言的内置特性,您也可以以自然的方式编写代码

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

您在 Nim 中没有获得很多“D 优势”。 它的吸引力在于其语法。

回复 ,作者:aberba

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

嗨。 我是 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

嗨 Ilya,很高兴看到您也喜欢 UFCS 的想法。 对我来说,它感觉很自然。

回复 ,作者:Ilya Sher (未验证)

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/ 可以让您在不安装的情况下进行试用。

您说得对! 我没有想到。 我会在我的下一篇文章中包含这一点。

回复 ,作者:Piotrek (未验证)

您说得对! 我没有想到。 我会在我的下一篇文章中包含这一点。

回复 ,作者:Piotrek (未验证)

正如您所提到的,它是语法糖。 提高代码可读性 - 使其更接近真正的英语。 那么为什么不
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

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.