使 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 很无聊,因为我转向了格式化和脚本语言,例如 Ruby 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.