D 中 OutputRange 和 put() 的目的是什么?

What is the purpose of OutputRange and put() in D?

我需要对 OutputRange 及其目的进行一些说明。它表示类似于发送到 stdout 的流式元素输出,需要支持 put() 方法,其中:

determines the capabilities of the range and the element at compile time and uses the most appropriate method to output the element.

output 元素但是到 wherewhat purpose?

import std.stdio;
import std.range;

void main() {
    int[] arr = [1, 2, 3, 4, 5];
    auto s = arr;
    writeln(s); // [1, 2, 3, 4, 5]
    s.put(100); // nothing is printed to stdout, should it?
    writeln(s); // [2, 3, 4, 5] 
} 

在上面的代码中,我们在切片上调用了 put(),因此我们丢失了 1,但是“100”去了哪里? MultiFile 示例感觉有点做作。 OutputRange 的更实际的用例会更好。

一件小事,为什么putput?在其他语言中,put 用于对某些集合进行插入或添加操作。我觉得很混乱。

更新: 看起来我们需要保留原始切片的副本以防止元素丢失。

int[] arr = [1, 2, 3, 4, 5];
auto s = arr;
s.put(100);
writeln(arr); // [100, 2, 3, 4 ,5];

我觉得上面的内容很混乱,也许我错过了OutputRange背后的概念:(

首先,阅读 put 的文档:http://dpldocs.info/experimental-docs/std.range.primitives.put.html

值得注意的是:

Tip: put should not be used "UFCS-style", e.g. r.put(e). Doing this may call R.put directly, by-passing any transformation feature provided by Range.put. put(r, e) is prefered.

所以不要调用 s.put(x),而是调用 put(s, x);

它还谈到了您的更新中发生的事情:

put treats dynamic arrays as array slices, and will call popFront on the slice after an element has been copied.

Be sure to save the position of the array before calling put.

您还会注意到文档经常使用 "copying" 这个词。那么,put 将物品放在哪里,为什么?

其中取决于输出范围。 put 是一个通用接口,可以根据目标对象执行各种操作。有些人可能会将其流式传输到标准输出,有些人可能会将其放入数据缓冲区,有些人可能会做一些完全不同的事情。

在您使用的数组切片的情况下,库将其解释为 固定大小的缓冲区 并且其 put 将数据复制到其中。

实现看起来像

copy element to buffer
advance buffer
update buffer's remaining space

这就是为什么你需要在开头保留对切片的单独引用,否则它会复制和前进,所以它看起来就像刚刚消失的东西。为什么它会这样做呢?

int[32] originalBuffer;
int[] buffer = originalBuffer[];
put(buffer, 5);
put(buffer, 6); // since the last one advanced the remaining space, this next call just works

最后实际使用了多少缓冲区?这是进阶的另一个用法:可以减去算出来:

int[] usedBuffer = originalBuffer[0 .. $ - buffer.length];

除了输出范围内剩下的 space 之外,我们只取出原始文件中的所有内容。

其他范围可能会保留内部计数。 http://dpldocs.info/experimental-docs/std.range.primitives.put.html#examples 处的文档示例显示了一个带有动态内部缓冲区的示例。

static struct A {
    string data;
    void put(C)(C c) if (isSomeChar!C) {
        data ~= c;
    }
}

它的 put 方法将字符复制到内部字符串中,因此它会根据需要增长,然后 data.length 告诉你它有多大。 (标准库的 appender 就像这样顺便说一句)

输出范围接口非常小 - 它真正需要的只是一个 put 函数,然后它没有指定你用它做什么。想象一下,如果它正在写入 stdout,那么长度并不重要,根本不需要 return 数据给用户。这也是它不使用 ~= 附加运算符的原因 - 它不一定附加任何内容。


所以回顾一下:它去了哪里,为什么?视对象而定! OutputRange/put 是经过深思熟虑的通用接口,旨在收集数据并用它来做……一些事情。它是一个最终目的地,因此不支持像其他范围一样的链接。

使用内置切片,它会向其中复制数据并提升位置以使其准备好接受更多数据。这需要你做更多的工作来跟踪它,但为通用使用提供了很大的灵活性和效率。尽管专门针对您的特定需求,但您可能会更好地使用其他功能。如果要追加,请尝试 http://dpldocs.info/appender 例如。