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

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

如果您对新语言感兴趣,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本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.