Инициализация динамического массива D, шаг и операция индекса

Извините, это стало 3-кратным вопросом относительно массивов

Я думаю, что (динамические) массивы действительно эффективны в D, но некоторое время меня беспокоило следующее:

В C++ я мог бы легко выделить массив с назначенными значениями, но в D я не нашел способа сделать это. Наверняка нет проблем:

int[] a = new int[N];
a[] = a0;

Но это выглядит неэффективно, так как первая строка будет инициализирована с 0, а вторая — с a0. Можно ли сделать что-то подобное следующему в D?

int[] a = new int(a0)[N]; // illegal

Еще один вопрос эффективности, который у меня есть при использовании шага в std.range:

import std.stdio;
import std.range;

struct S
{
    int x;

    this(this)
    {
        writeln("copy ", x);
    }
}

void f(S[] s)
{
}

int main()
{
    S[] s = new S[10];
    foreach (i, ref v; s)
    {
        v.x = i;
    }

    f(stride(s, 3)); // error
    return 0;
}

Конечно, я был наивен, думая, что могу просто использовать шаг для создания нового массива без копирования его элементов? В D это невозможно сделать, верно?


Итак, я пошел и смоделировал, как если бы массив был таким, как возвращает шаг, и реализовал f как:

f(s, 3);

void f(S[] s, uint stride)
{
    ref S get(uint i)
    {
        assert (i * stride < s.length);
        return s[i * stride];
    }

    for (uint x ... )
    {
        get(x) = ...;
    }
}

Можно ли вместо этого написать get(x) с помощью оператора индекса get[x]? Таким образом, я мог бы статически смешать/включить шагающую функцию get и сохранить остальную часть функции аналогичной. Мне был бы интересен выбранный подход, поскольку локальной структуре не разрешен доступ к переменным области видимости функции (почему бы и нет?).


person Taco de Wolff    schedule 02.12.2011    source источник


Ответы (2)


Но это выглядит неэффективно, так как первая строка будет инициализирована 0, а 2 — а0. Можно ли сделать что-то подобное следующему в D?

Используйте 1_

S[] s = uninitializedArray!(S[])(N);
s[] = a0; 

Конечно, я был наивен, думая, что могу просто использовать шаг для создания нового массива без копирования его элементов? В D это невозможно сделать, верно?

Ваша функция f имеет в качестве аргумента S[], который отличается от того, что возвращает stride. D-способ решить эту проблему — заставить вашу функцию f принимать любой диапазон, сделав ее шаблоном:

void f(Range)(Range s)
{
    foreach (item; s)
        // use item
}

S[] s = new S[10];
f(s); // works
f(stride(s, 3)); // works too

В качестве альтернативы вы можете скопировать массив:

f(array(stride(s, 3)));

Но вы, вероятно, захотите избежать копирования всего массива, если он большой.


Можно ли вместо этого написать get(x) с помощью оператора индекса get[x]? Таким образом, я мог бы статически смешать/включить функцию получения шага и сохранить остальную часть функции аналогичной. Мне был бы интересен выбранный подход, поскольку локальной структуре не разрешен доступ к переменным области видимости функции (почему бы и нет?).

Вы можете перегрузить оператор индексации в своей собственной структуре.

struct StrideArray
{
    this(S[] s, uint stride) { m_array = s; m_stride = stride; }

    S opIndex(size_t i) { return s[i * m_stride]; }
    void opIndexAssign(size_t i, S value) { s[i * m_stride] = value; }

    private S[] m_array;
    private uint m_stride;
}

Это (отчасти) то, как работает настоящая функция stride. Я бы рекомендовал ознакомиться с диапазонами.

person Peter Alexander    schedule 03.12.2011
comment
Спасибо за чтение. Хотя с небольшой потерей производительности в некоторых конструкциях (возможно, я злоупотребляю ими ...), код намного чище и универсальнее. Почему разница в opIndex и opIndexAssign, не может ли opIndex просто вернуть ссылку? - person Taco de Wolff; 03.12.2011
comment
Проблема с производительностью решена. После преобразования массива из библиотеки C из указателя в диапазон код стал еще чище. Разницы в скорости не заметил. - person Taco de Wolff; 03.12.2011
comment
void f(Range)(...) должно быть ограничено. isInputRange ? - person Alexander Malakhov; 09.12.2011
comment
@ Александр: Зависит от того, что он хочет сделать. Если он хочет записать в диапазон, то входной диапазон не годится. - person Peter Alexander; 09.12.2011
comment
Сейчас я ограничиваю это с помощью isForwardRange. Мне нужно читать и записывать значения независимо. - person Taco de Wolff; 10.12.2011

вы можете дублировать (создать копию) массива с помощью .dup (это также будет работать со слайсами) или вы можете установить элементы с помощью инициализатора массива

int[] a=a0.dup;
int[] b=[e1,e2,e3];

вы можете сделать f универсальным (stride() возвращает структуру, которую вы можете перебирать, а не массив)

void f(Z)(Z s)if(isInputRange!Z){
    foreach(elem;s){
         //...
    }
}

помните, что массивы по сути являются структурами с полем указателя на некоторый блок памяти и полем размера

person ratchet freak    schedule 03.12.2011
comment
Если a0 является значением, элемент dup не существует. И делать: int[] b=[a0, a0, a0, a0 ... (N times)]; это каторга. Таким образом, нет способа выделить массив, используя что-либо, кроме конструктора по умолчанию для элементов (например, установка int со значением, кроме 0, для структур одно и то же). - person Taco de Wolff; 03.12.2011