4

Sorry, this became a 3-fold question regarding arrays

I think (dynamic) arrays are truly powerful in D, but the following has been bothering me for a while:

In C++ I could easily allocate an array with designated values, but in D I haven't found a way to do so. Surely the following is no problem:

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

But it looks inefficient, since line one will initialize with 0, and like 2 with a0. Could something similar to the following be done in D?

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

Another efficiency matter I have when using stride in 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;
}

Surely I was naive thinking I could simply use stride to create a new array without copying it's elements? There is no way to do so in D, right?


So I went and simulated as if the array was as stride would return, and implemented f as:

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) = ...;
    }
}

Would there be a way to instead write get(x) using the index operator get[x]? This way I could statically mixin / include the striding get function and keep the rest of the function similar. I'd be interested in the approach taken, since a local struct is not allowed to access function scope variables (why not?).

Taco de Wolff
  • 1,682
  • 3
  • 17
  • 34

2 Answers2

7

But it looks inefficient, since line one will initialize with 0, and like 2 with a0. Could something similar to the following be done in D?

Use std.array.uninitializedArray

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

Surely I was naive thinking I could simply use stride to create a new array without copying it's elements? There is no way to do so in D, right?

Your function f has an S[] as an argument, which is different from what stride returns. The D way to solve this is to make your f function accept any range by making it a template:

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

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

Alternatively you can copy the array:

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

But you probably want to avoid copying the entire array if it is large.


Would there be a way to instead write get(x) using the index operator get[x]? This way I could statically mixin / include the striding get function and keep the rest of the function similar. I'd be interested in the approach taken, since a local struct is not allowed to access function scope variables (why not?).

You can overload the indexing operator in your own struct.

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;
}

This is (kind of) the way the actual stride function works. I'd recommend reading up on Ranges.

Peter Alexander
  • 53,344
  • 14
  • 119
  • 168
  • Thank you for the read. Although with slight performance loss in certain constructs (perhaps I abuse them..), the code is much cleaner and generic. Why the difference in opIndex and opIndexAssign, can't opIndex simply return a ref? – Taco de Wolff Dec 03 '11 at 12:41
  • Performance issue resolved. After converting an array from a C lib from a pointer to a Range, code got even cleaner. No speed difference noticed. – Taco de Wolff Dec 03 '11 at 15:55
  • `void f(Range)(...)` should be constrained. `isInputRange` ? – Alexander Malakhov Dec 09 '11 at 02:13
  • @Alexander: Depends what he wants to do. If he wants to write to the range then an input range is no good. – Peter Alexander Dec 09 '11 at 10:52
  • I constrain it with isForwardRange now. I need to read and write to the values independently. – Taco de Wolff Dec 09 '11 at 22:30
1

you can duplicate (create a copy of) an array with .dup (this will also work with slices) or you can set the elements with the array initializer

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

you can make the f generic (stride() returns a struct that you can iterate over, not an array)

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

remember that arrays are essentially structs with a pointer field to some memory block and a size field

ratchet freak
  • 47,288
  • 5
  • 68
  • 106
  • If a0 is a value, the dup member does not exist. And doing: `int[] b=[a0, a0, a0, a0 ... (N times)];` is hard labour. So there is no way to allocate an array using anything but the default constructor for the elements (such as setting int's with a value but 0, for structs the same thing). – Taco de Wolff Dec 03 '11 at 12:48