5

如何将一行读取为 D 中的范围?

我知道 D 中有范围,但我只是想知道如何使用这个概念简单地迭代字符串的每个字符?

为了显示我所追求的,Go 中的类似代码是:

for _, someChar := range someString {
    // Do something
}
4

2 回答 2

12

这取决于您是否要迭代代码单元或代码点。语言本身通过数组元素对数组进行迭代,而字符串是代码单元的数组,所以如果你只是简单地使用foreachwith 类型推断,那么 with

foreach(c; "La Verité")
    writeln(c);

打印的最后两个字符将是乱码,因为é它是由两个 UTF-8 代码单元组成的代码点,并且您正在打印单个代码单元(因为char是 UTF-8 代码单元)。然而,如果你这样做

foreach(dchar c; "La Verité")
    writeln(c);

然后运行时会将代码单元解码为代码点,并将é作为最后一个字符打印。但这些都不是真正将字符串作为范围操作。foreach无需使用输入范围 API 即可对数组进行原生操作。但是,对于所有字符串类型,范围 API 看起来像

@property bool empty();
@property dchar front();
void popFront();

它对字符串进行操作,而dchar不是它们的代码单元类型。这避免了诸如std.algorithm.filter在单个代码单元上操作等功能的问题,因为这没有任何意义。对代码点进行操作也不是 100% 正确,因为 Unicode 在组合代码点和字素等方面变得非常复杂,但是对代码点进行操作更接近于正确(而且我相信在增加范围方面正在进行工作在您需要并愿意支付性能损失的情况下,将字素支持到标准库中)。因此,让字符串的范围 API 对它们进行操作dchar是更正确的,如果你做了类似的事情

foreach(c; filter!"true"("La Verité"))
    writeln(c);

你会迭代dchar,并且é会正确打印。当然,所有这些的缺点是foreach字符串默认在代码单元级别上操作,而字符串的范围 API 将它们作为代码点操作,因此在混合数组操作和基于范围的操作时必须小心在弦上。这也是为什么string并且wstring不被视为随机访问范围 - 只是双向范围。当代码点由不同数量的代码单元组成时,您不能在 O(1) 中对代码点进行随机访问(而随机访问范围dstring 随机访问范围,因为使用 UTF-32,每个代码单元都是一个代码点) .

于 2013-05-16T17:25:37.670 回答
1
foreach(ch; str)
    do_something(ch);

一个字符串是一个InputRange. AnInputRange实现了三件事:

  • 空的; 是空的吗?
  • 正面; 给我下一个项目。
  • 流行前线;推进范围,否则 front 将返回相同的值。

foreach “了解”如何使用范围,因此它“正常工作”。

但我不会说 Go,所以我不完全确定我们说的是同一种语言。

于 2013-05-16T14:57:49.230 回答