For и foreach в D

Помимо синтаксических различий, являются ли они по сути одинаковыми? Оба они реализованы на основном языке? или foreach часть стандартной библиотеки? Что же касается производительности, то это имеет значение, если я выбираю один за другим?

Ответы

Ответ 1

Вы всегда должны использовать 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]++; }
    

    нет, потому что компилятор автоматически выбирает правильный тип для итерации. Это также улучшает читаемость.

Ответ 2

Основное различие между foreach и for - это более высокий уровень абстракция a foreach -loop. A 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 или какой-либо другой механизм в случае класса или структуры.
  • Специальные функции. итерация по типам кортежей, декодирование UTF-8 и Строки UTF-16.
  • Производительность: foreach -loop позволяет компилятору решить, как оптимально реализовать цикл на основе типа (итерации по массиву, диапазону, строка, объект...) и, возможно, другую информацию (например, размер тип). Это обеспечивает эффективную реализацию для всех типов и другого компилятора оптимизация без необходимости слишком беспокоиться о реализации Детали. В действительности производительность foreach относительно ручной кодировки for смешивается. foreach(dchar c; someString) {...} (т.е. декодирование UTF-8 string while looping) очень быстро. Но foreach(a; someObject) {...}, где someObject реализует opApply, немного медленнее (поскольку тело цикла завернутый в делегат, и opApply обычно вызывает этот делегат внутри цикла, который генерирует некоторые накладные расходы). Как обычно, это не имеет значения для вашего кода в 99,99% случаи, так как foreach всегда будет давать (по крайней мере) приличную реализацию.

Основной недостаток (к тому же, иногда, скорость) заключается в том, что некоторые вещи не могут выполняются с помощью foreach, а именно: многие мутации вещи, зацикленной над (например, изменение размера массива внутри тела цикла).