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

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

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

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

回复 ,作者 jyapayne(未验证)

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

回复 ,作者 aberba

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

您好。我是 Next Generation 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()
}

---

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