使 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 很合理,尤其是与多重分发结合使用时,才将 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 and Rails 和 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.