如何在D语言中将一行读取为范围?
我知道D语言中有range概念,但我想知道如何使用这个概念来简单迭代字符串的每个字符?
为了说明我的需求,Go语言中类似的代码是:
for _, someChar := range someString {
// Do something
}
如何在D语言中将一行读取为范围?
我知道D语言中有range概念,但我想知道如何使用这个概念来简单迭代字符串的每个字符?
为了说明我的需求,Go语言中类似的代码是:
for _, someChar := range someString {
// Do something
}
这取决于你想要迭代代码单元还是代码点。语言本身通过数组元素迭代数组,字符串是代码单元的数组,因此,如果你只是使用类型推断的foreach
,那么就会迭代代码单元。
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
这样的函数在操作单个代码单元时可能出现的问题,因为那没有意义。操作代码点也不完全正确,因为Unicode在组合代码点和字形方面变得非常复杂,但是操作代码点要更接近正确(我相信正在对标准库进行添加支持字形范围的工作,以便在需要并愿意支付性能代价的情况下使用)。因此,将范围API用于字符串,并将其作为dchar
范围操作,更加正确,如果你执行以下操作:
foreach(c; filter!"true"("La Verité"))
writeln(c);
你需要迭代dchar
,这样é
才会正确打印。当然,所有这些的缺点在于,默认情况下字符串上的foreach
是按照代码单元级别操作的,而字符串的范围API则按照它们作为代码点进行操作,因此在混合使用数组操作和基于范围的字符串操作时必须小心。这也是为什么string
和wstring
不被视为随机访问范围 - 只是双向范围。当由多个代码单元组成时,无法以O(1)访问代码点,而dstring
是一个随机访问范围,因为对于UTF-32,每个代码单元都是一个代码点。
foreach
将只使用数组API。对于数组,必须显式地使用基于范围的函数。实际上,尽管foreach
的不一致性很烦人,但使其行为取决于是否导入了std.array
会非常容易出错,因为仅添加或删除该导入可能会极大地改变代码的行为,具体取决于它正在做什么。 - Jonathan M Davisforeach(ch; str)
do_something(ch);
字符串是一个 InputRange
。一个 InputRange
实现了三个东西:
foreach 知道如何处理范围,所以它 "只是工作"。
但我不会说 Go 语言,所以我不确定我们是否在说同一种语言。