【问题标题】:for and foreach statements in DD 中的 for 和 foreach 语句
【发布时间】:2011-04-12 03:37:59
【问题描述】:

除了句法上的差异之外,两者本质上是相同的吗?它们都是用核心语言实现的吗?还是foreach 是标准库的一部分?就性能而言,如果我选择一个而不是另一个会有什么不同吗?

【问题讨论】:

    标签: d


    【解决方案1】:

    如果可能,您应该始终使用foreach

    1. foreach 迭代几乎任何东西(甚至是元数据,如编译时数据类型); for不能。

      foreach (Type; TypeTuple!(int, long, short)) { pragma(msg, Type); }
      

      但你不能用 for 循环来做到这一点。

    2. 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。)

    3. foreach 可以用opApply 和范围结构重载,但for 不能。这在迭代树结构(如文件系统中的所有文件夹)时非常方便,因为它实际上允许您使用完全基于堆栈的内存,而不是在堆(因为你可以使用递归)。

    4. foreach 在大多数情况下是首选,因为它可以避免您显式键入数据,这对于防止一些错误很有用。例如,

      for (int i = 0; i < n; i++) { arr[i]++; }
      

      如果n 大于 2^32 - 1 则很危险,但是

      foreach (i; 0 .. n) { arr[i]++; }
      

      不是,因为编译器会自动选择正确的迭代类型。这也提高了可读性。

    【讨论】:

    • 你知道foreach对于不是范围的数组是如何降低的吗?如何显式访问数组迭代器?很抱歉在评论中提问。我想,这和这个OP有关。
    • 嗯,我开始阅读 Ali Çehreli 书中关于范围的章节,他指出可以通过减少切片长度来完成对数组的迭代,我注意到切片固定大小array 将其转换为与范围兼容的迭代器。使用array[0..$] 是在固定大小的数组上获得范围兼容的“迭代器”的最简单方法(但要注意不要从中删除元素,这会导致 GC 分配的副本破坏切片和数组之间的元素共享)。
    【解决方案2】:

    foreachfor 的主要区别在于更高级别 foreach-loop 的抽象。 foreach-loop 通常会降低到一些 for-loop 由编译器生成。这有(至少)四个优点:

    1. 可读性foreach (a; someArray) doSomething(a); 本质上更 比for (size_t i = 0; i &lt; someArray.length; i++) doSomething(someArray[i]); 可读。 如果someArray 的类型不是一个简单的数组,这一点就更清楚了。
    2. 灵活性:如果在某个时间点,您决定 someArray 必须从某个数组更改为范围或对象 (例如,实现并行循环),foreach 保持不变,而 for-loop 必须更改为使用 emptyfrontpopFront (在范围的情况下)或opApply 或在 类或结构。
    3. 特殊功能,例如迭代类型元组,解码 UTF-8 和 UTF-16 字符串。
    4. 性能foreach-loop 让编译器决定如何优化 根据类型实现循环(遍历数组、范围、 一个字符串,一个对象...)和可能的其他信息(例如 类型)。这允许有效实现所有类型和其他编译器 无需过多担心实现的优化 细节。实际上,foreach 的性能相对于手工编码的for 是混合的。 foreach(dchar c; someString) {...}(即 UTF-8 的解码 循环时的字符串)非常快。但是foreach(a; someObject) {...},在哪里 someObject 实现 opApply,速度有点慢(因为循环体是 包装到一个委托中,opApply 通常在一个循环中调用这个委托,这 产生一些开销)。像往常一样,这在 99.99% 的情况下对您的代码无关紧要 这些案例,因为foreach 总是会产生(至少)体面的实现。

    主要缺点(除了,偶尔,速度)是有些东西不能 用foreach 完成,即被循环的事物的许多突变 (例如,在循环体内调整数组的大小)。

    【讨论】:

      猜你喜欢
      • 2012-11-05
      • 1970-01-01
      • 2021-08-27
      • 1970-01-01
      • 2019-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-21
      相关资源
      最近更新 更多