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
, а именно: многие мутации вещи, зацикленной над
(например, изменение размера массива внутри тела цикла).