2012-03-29 4 views
7

Рассмотрим следующий код:d2: назначая диапазоны/итераторы срезах массива

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) не работает для диапазонов - есть ли обходные пути? Я думаю, причина такая же, как и почему диапазоны не могут быть назначены средам массива.

ответ

11

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

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

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

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

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

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

Что касается вопроса о необходимости среза статических массивов, вы действительно должны спросить об этом как отдельный вопрос, но два существующих вопроса, которые относятся к нему: this one и this one. В значительной степени это связано с тем, что IFTI (Impression Function Template Instantiation) создает экземпляр, используя точный тип, который ему задан, а статический массив не является ни динамическим массивом, ни диапазоном, поэтому любая шаблонная функция, которая требует определенного динамического массива или диапазон не сможет скомпилироваться со статическим массивом. Компилятор будет неявно срезать статические массивы для преобразования их в динамические массивы для функций, которые явно принимают динамические массивы, но такого рода неявные преобразования не происходят с созданием шаблона, поэтому вы должны явно срезать статические массивы, чтобы передать их в диапазон - основанные функции.

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

foreach(elem; range) 
{ 
    //stuff 
} 

получает опускают на что-то близко к

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

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

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

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

+0

Спасибо, Джонатан! Я знаю, что диапазоны и массивы являются разными типами, но полагал, что D может реализовать какое-то неявное преобразование, поскольку диапазоны конвертируются в массивы, если они конечны, а их элементы совместимы с типами массива. Я знаю разницу в распределении стека и кучи, а также вычисление и память накладных расходов на массирование, но спасибо за предупреждение в любом случае :) Также спасибо за разъяснение с IFTI - если бы я понял это правильно, в будущем это может измениться чтобы позволить неявные статические массивы среза, но теперь мы должны использовать этот путь из-за несовершенства. – toriningen

+0

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

Смежные вопросы