【发布时间】:2011-04-12 03:37:59
【问题描述】:
除了句法上的差异之外,两者本质上是相同的吗?它们都是用核心语言实现的吗?还是foreach 是标准库的一部分?就性能而言,如果我选择一个而不是另一个会有什么不同吗?
【问题讨论】:
标签: d
除了句法上的差异之外,两者本质上是相同的吗?它们都是用核心语言实现的吗?还是foreach 是标准库的一部分?就性能而言,如果我选择一个而不是另一个会有什么不同吗?
【问题讨论】:
标签: d
如果可能,您应该始终使用foreach。
foreach 迭代几乎任何东西(甚至是元数据,如编译时数据类型); for不能。
foreach (Type; TypeTuple!(int, long, short)) { pragma(msg, Type); }
但你不能用 for 循环来做到这一点。
foreach 可用于在编译时执行操作(上述扩展);例如,如果你有一段重复 10 次的代码,你可以说:
template Iota(size_t a, size_t b) //All integers in the range [a, b)
{
static if (a < b) { alias TypeTuple!(a, Iota!(a + 1, b)) Iota; }
else { alias TypeTuple!() Iota; }
}
foreach (i; Iota!(0, 10)) { int[i] arr; } //Not possible with 'for'
这将在编译时发生,i 被视为常量。 (这通常不适用于for。)
foreach 可以用opApply 和范围结构重载,但for 不能。这在迭代树结构(如文件系统中的所有文件夹)时非常方便,因为它实际上允许您使用完全基于堆栈的内存,而不是在堆(因为你可以使用递归)。
foreach 在大多数情况下是首选,因为它可以避免您显式键入数据,这对于防止一些错误很有用。例如,
for (int i = 0; i < n; i++) { arr[i]++; }
如果n 大于 2^32 - 1 则很危险,但是
foreach (i; 0 .. n) { arr[i]++; }
不是,因为编译器会自动选择正确的迭代类型。这也提高了可读性。
【讨论】:
foreach对于不是范围的数组是如何降低的吗?如何显式访问数组迭代器?很抱歉在评论中提问。我想,这和这个OP有关。
array[0..$] 是在固定大小的数组上获得范围兼容的“迭代器”的最简单方法(但要注意不要从中删除元素,这会导致 GC 分配的副本破坏切片和数组之间的元素共享)。
foreach 和for 的主要区别在于更高级别
foreach-loop 的抽象。 foreach-loop 通常会降低到一些
for-loop 由编译器生成。这有(至少)四个优点:
foreach (a; someArray) doSomething(a); 本质上更
比for (size_t i = 0; i < someArray.length; i++) doSomething(someArray[i]); 可读。
如果someArray 的类型不是一个简单的数组,这一点就更清楚了。someArray 必须从某个数组更改为范围或对象
(例如,实现并行循环),foreach 保持不变,而
for-loop 必须更改为使用 empty、front 和 popFront
(在范围的情况下)或opApply 或在
类或结构。foreach-loop 让编译器决定如何优化
根据类型实现循环(遍历数组、范围、
一个字符串,一个对象...)和可能的其他信息(例如
类型)。这允许有效实现所有类型和其他编译器
无需过多担心实现的优化
细节。实际上,foreach 的性能相对于手工编码的for
是混合的。 foreach(dchar c; someString) {...}(即 UTF-8 的解码
循环时的字符串)非常快。但是foreach(a; someObject) {...},在哪里
someObject 实现 opApply,速度有点慢(因为循环体是
包装到一个委托中,opApply 通常在一个循环中调用这个委托,这
产生一些开销)。像往常一样,这在 99.99% 的情况下对您的代码无关紧要
这些案例,因为foreach 总是会产生(至少)体面的实现。主要缺点(除了,偶尔,速度)是有些东西不能
用foreach 完成,即被循环的事物的许多突变
(例如,在循环体内调整数组的大小)。
【讨论】: