d2: назначение диапазонов / итераторов для срезов массива - PullRequest
7 голосов
/ 30 марта 2012

Рассмотрим следующий код:

enum size = 16;
double[size] arr1 = [...];
double[size] arr2 = [...];
process = (double x) { return (x + 1); };

arr2[] = map!(process)(arr1[]); // here

У меня проблемы с преобразованием результатов map обратно в мой простой массив.Проблема относится не только к map, но также к take, repeat и ко всем тем точным инструментам из std.algorithm и std.range, которые работают в диапазонах.

В этом задании я получаю Error: cannot implicitly convert expression (map(arr1[])) of type Result to double[].Как я могу оценить диапазон до массива, не используя

uint i = 0;
foreach (x; map!(process)(arr1[])) {
    arr2[i] = x;
    i++;
}

?

Кроме того, кто-то может объяснить, почему я должен вызывать map!(process)(arr1[]) вместо map!(process)(arr1) со статическими массивами?Разве статические массивы не должны быть совместимы с динамическими для итераций, или я ничего не понимаю?

Кроме того, кажется, что простой синтаксис перечисления foreach (index, item; sequence) не работает для диапазонов - есть ли обходные пути?Я предполагаю, что причина такая же, как то, почему диапазоны не могут быть назначены срезами массива.

1 Ответ

11 голосов
/ 30 марта 2012

Такие функции, как map и filter, возвращают диапазоны, а не массивы, поэтому простое назначение массива не будет работать так же, как назначение string для wstring.Они разные типы.А для многих основанных на диапазоне функций (включая map и filter) возвращаемые диапазоны фактически ленивы, чтобы избежать ненужных вычислений, что делает их гораздо менее совместимыми с массивом.Решение состоит в том, чтобы использовать std.array.array, который берет диапазон и создает из него динамический массив.Таким образом, вы можете сделать

auto arr = array(map!process(origArray));

Однако я бы посоветовал не преобразовывать диапазон в массив до того, как вам это действительно понадобится, поскольку это может привести к ненужным вычислениям, а это означает выделение нового массива.Если вам действительно нужен массив, то непременно используйте std.array.array для преобразования диапазона, но работа с диапазоном часто может быть более эффективной, если вам не нужен реальный массив.Однако, если вы хотите преобразовать результат в статический массив, а не в динамический, вам, вероятно, лучше просто назначить каждый элемент в цикле (и, возможно, пропустить map в целом), так какс помощью std.array.array будет выделен динамический массив, который вы не будете использовать после назначения статического массива.Это пустая трата памяти.

Кроме того, помните, что использование статических массивов с функциями на основе диапазона может быть рискованным в том смысле, что они должны разрезать статический массив, чтобы получить динамический массив для обработки функций на основе диапазона,и если этот динамический массив выходит за пределы области, в которой был объявлен статический массив, то вы пропускаете ссылки на данные, которых больше не существует.Например,

auto func()
{
    int[5] arr;
    return map!process(arr[]);
}

было бы очень плохо.Однако, если вы закончили использовать срез, и к нему больше ничего не относится (включая любые диапазоны, которые могли быть созданы), прежде чем вы выйдете из области видимости со статическим массивом в нем, все будет в порядке.Это - это что-то, к чему следует быть осторожным.

Что касается вопроса о необходимости нарезать статические массивы, вам действительно следует задать это как отдельный вопрос, но два существующих вопроса, которые относятся к нему. это и это .То, к чему это в значительной степени сводится, заключается в том, что IFTI (создание шаблона неявной функции) создает экземпляр, используя именно тот тип, который он задал, и статический массив не является ни динамическим массивом, ни диапазоном, поэтому любая шаблонная функция, для которой конкретно требуется динамический массив илидиапазон не будет компилироваться со статическим массивом.Компилятор будет неявно разрезать статические массивы для преобразования их в динамические массивы для функций, которые явно принимают динамические массивы, но такого рода неявные преобразования не происходят с созданием экземпляра шаблона, поэтому вы должны явно разрезать статические массивы для передачиих для функций, основанных на диапазонах.

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

foreach(elem; range)
{
    //stuff
}

понижается до уровня, близкого к

for(; !range.empty; range.popFront())
{
    auto elem = range.front;
    //stuff
}

И это вовсе не включает индексы.Это может быть изменено для создания индексной переменной для вас, но не всегда имеет смысл, чтобы диапазоны повторяли свой индекс по одному на каждой итерации (как обычно, было бы хорошо),и так, что не было сделано.Однако достаточно просто добавить свой собственный счетчик.

{
    size_t i;
    foreach(elem; range)
    {
        //stuff
        ++i;
    }
}

opApply поддерживает использование индексов с foreach, но это не диапазон, и он не работает с функциями на основе диапазона.

...