digitalmars.D.learn - Auto recursive function

• Razvan Nitu (35/35) Jan 11 2017 Hi,
• Stefan Koch (5/11) Jan 11 2017 I believe you should not do this with recursion.
• =?UTF-8?Q?Ali_=c3=87ehreli?= (30/43) Jan 11 2017 Agreed but the question is still valid. I had similar cases before where...
• Martin Nowak (6/8) Jan 11 2017 Well, choose is the right tool when the choice can only be made
• Razvan Nitu (8/23) Jan 11 2017 Maybe you want to allocate memory for more complex data
• Martin Nowak (2/6) Jan 11 2017 Looks like `static if` would fix your specific problem.
• Ignacious (12/48) Jan 11 2017 This is probably not possible. You are trying to have multiple
• Ignacious (24/60) Jan 11 2017 If you change the return type to a void* your code basically
• Bauss (4/31) Jan 12 2017 That isn't a solution and will probably introduce other bugs.
• thedeemon (26/30) Jan 12 2017 You were very close to the answer:
```Hi,

I am currently trying to create a function
makeMultidimensionalArray which allocates memory for a
multidimensional array. It is very similar with ,
the difference being that it is uninitialized. Here is the code:

auto makeMultidimensionalArray(T, Allocator)(auto ref Allocator
alloc, size_t[] lengths)
{
if (lengths.length == 1)
{
return makeArray!T(alloc, lengths);
}
else
{
alias E = typeof(makeMultidimensionalArray!T(alloc,
lengths[1..\$]));
auto ret = makeArray!E(alloc, lengths);
foreach (ref e; ret)
e = makeMultidimensionalArray!T(alloc, lengths[1..\$]);
return ret;
}
}

The lengths[] specifies the lengths for each dimension. The
problem with this code is that auto is going to be evaluated to
T[] for the first time and when it
recurs, creating T[][] I get the error "mismatched function
return type inference of T[][] and T[]". Is there a way to
surpass that? I saw that in 
the recursive call is done by prefixing the function name with a
'.'; I tried that but it doesn't work. I must be missing
something, any ideas?

Thanks,
RazvanN


https://github.com/dlang/phobos/blob/master/std/experimental/ndslice/slice.d#L834
```
Jan 11 2017
```On Wednesday, 11 January 2017 at 19:23:10 UTC, Razvan Nitu wrote:
Hi,

I am currently trying to create a function
makeMultidimensionalArray which allocates memory for a
multidimensional array. It is very similar with ,
the difference being that it is uninitialized. Here is the code:

[...]

I believe you should not do this with recursion.
Because that'll instantiate the template multiple times with
different types.
What is wrong with using ndslice here ?
```
Jan 11 2017
```On 01/11/2017 11:30 AM, Stefan Koch wrote:
On Wednesday, 11 January 2017 at 19:23:10 UTC, Razvan Nitu wrote:
Hi,

I am currently trying to create a function makeMultidimensionalArray
which allocates memory for a multidimensional array. It is very
similar with ,
the difference being that it is uninitialized. Here is the code:

[...]

I believe you should not do this with recursion.
Because that'll instantiate the template multiple times with different
types.
What is wrong with using ndslice here ?

Agreed but the question is still valid. I had similar cases before where
std.range.choose looked like a solution but did not work. Here's my
current attempt, which fails:

import std.stdio;
import std.experimental.allocator.gc_allocator;
import std.experimental.allocator;

auto one(T, Allocator)(auto ref Allocator alloc, size_t length) {
return makeArray!T(alloc, length);
}

auto two(T, Allocator)(auto ref Allocator alloc, size_t[] lengths) {

// Error: forward reference to inferred return type of function call
'makeMultidimensionalArray!int(alloc, lengths[1..__dollar])'

alias E = typeof(makeMultidimensionalArray!T(alloc, lengths[1..\$]));
auto ret = makeArray!E(alloc, lengths);
foreach (ref e; ret)
e = makeMultidimensionalArray!T(alloc, lengths[1..\$]);
return ret;
}

auto makeMultidimensionalArray(T, Allocator)(auto ref Allocator alloc,
size_t[] lengths)
{
import std.range: choose;

return choose(lengths.length == 1, one!T(alloc, lengths),
two!T(alloc, lengths));
}

void main() {
writeln(makeMultidimensionalArray!int(GCAllocator.instance, [3, 4]));
}

Ali
```
Jan 11 2017
```On Wednesday, 11 January 2017 at 19:39:17 UTC, Ali Çehreli wrote:
return choose(lengths.length == 1, one!T(alloc,
lengths), two!T(alloc, lengths));

Well, choose is the right tool when the choice can only be made
at runtime. That would be uncommon for dimensionality.

Anyhow mentioning ndslice for multi-dim seems like the sanest tip
here.
https://github.com/libmir/mir
```
Jan 11 2017    Razvan Nitu <razvan.nitu1305 gmail.com> writes:
```On Wednesday, 11 January 2017 at 19:30:05 UTC, Stefan Koch wrote:
On Wednesday, 11 January 2017 at 19:23:10 UTC, Razvan Nitu
wrote:
Hi,

I am currently trying to create a function
makeMultidimensionalArray which allocates memory for a
multidimensional array. It is very similar with ,
the difference being that it is uninitialized. Here is the
code:

[...]

I believe you should not do this with recursion.
Because that'll instantiate the template multiple times with
different types.
What is wrong with using ndslice here ?

Maybe you want to allocate memory for more complex data
structures;
Using the function I am implementing, you will have the
multidimensional
array initialized with the default values; you don't have to pass
a slice
from which the initialization data is taken.
```
Jan 11 2017
```On Wednesday, 11 January 2017 at 19:23:10 UTC, Razvan Nitu wrote:
auto makeMultidimensionalArray(T, Allocator)(auto ref Allocator
alloc, size_t[] lengths)
{
if (lengths.length == 1)

Looks like `static if` would fix your specific problem.
```
Jan 11 2017
```On Wednesday, 11 January 2017 at 19:23:10 UTC, Razvan Nitu wrote:
Hi,

I am currently trying to create a function
makeMultidimensionalArray which allocates memory for a
multidimensional array. It is very similar with ,
the difference being that it is uninitialized. Here is the code:

auto makeMultidimensionalArray(T, Allocator)(auto ref Allocator
alloc, size_t[] lengths)
{
if (lengths.length == 1)
{
return makeArray!T(alloc, lengths);
}
else
{
alias E = typeof(makeMultidimensionalArray!T(alloc,
lengths[1..\$]));
auto ret = makeArray!E(alloc, lengths);
foreach (ref e; ret)
e = makeMultidimensionalArray!T(alloc,
lengths[1..\$]);
return ret;
}
}

The lengths[] specifies the lengths for each dimension. The
problem with this code is that auto is going to be evaluated to
T[] for the first time and when it
recurs, creating T[][] I get the error "mismatched function
return type inference of T[][] and T[]". Is there a way to
surpass that? I saw that in 
the recursive call is done by prefixing the function name with
a '.'; I tried that but it doesn't work. I must be missing
something, any ideas?

Thanks,
RazvanN


https://github.com/dlang/phobos/blob/master/std/experimental/ndslice/slice.d#L834

This is probably not possible. You are trying to have multiple
return types for the same function. You are thinking that each
recursive call is a new template but that doesn't seem to be the
case.

Instead, maybe try using string mixins to generate the
allocations. Should be quite easy and will work.

You could also try to use a helper function that you pass the
fully declared array(all dimensions) and the helper function then
allocates each dimension recursively... The difference is that
you are not passing around/returning sub-arrays so you don't have
```
Jan 11 2017
```On Wednesday, 11 January 2017 at 19:23:10 UTC, Razvan Nitu wrote:
Hi,

I am currently trying to create a function
makeMultidimensionalArray which allocates memory for a
multidimensional array. It is very similar with ,
the difference being that it is uninitialized. Here is the code:

auto makeMultidimensionalArray(T, Allocator)(auto ref Allocator
alloc, size_t[] lengths)
{
if (lengths.length == 1)
{
return makeArray!T(alloc, lengths);
}
else
{
alias E = typeof(makeMultidimensionalArray!T(alloc,
lengths[1..\$]));
auto ret = makeArray!E(alloc, lengths);
foreach (ref e; ret)
e = makeMultidimensionalArray!T(alloc,
lengths[1..\$]);
return ret;
}
}

The lengths[] specifies the lengths for each dimension. The
problem with this code is that auto is going to be evaluated to
T[] for the first time and when it
recurs, creating T[][] I get the error "mismatched function
return type inference of T[][] and T[]". Is there a way to
surpass that? I saw that in 
the recursive call is done by prefixing the function name with
a '.'; I tried that but it doesn't work. I must be missing
something, any ideas?

Thanks,
RazvanN


https://github.com/dlang/phobos/blob/master/std/experimental/ndslice/slice.d#L834

If you change the return type to a void* your code basically
works.

void* makeMultidimensionalArray(T, Allocator)(auto ref Allocator
alloc, size_t[] lengths)
{
if (lengths.length == 1)
{
int x = 0x01FEEF01;
return cast(void*)makeArray!T(alloc, lengths, x);
}
else
{
alias E = typeof(makeMultidimensionalArray!T(alloc,
lengths[1..\$]));

auto ret = makeArray!E(alloc, lengths);
foreach (ref e; ret)
e = makeMultidimensionalArray!T(alloc, lengths[1..\$]);
return cast(void*)ret;
}
}

The problem is that then you need to cast back and that
essentially results in the original problem. Can be done but
probably gonna have to use string mixins.
```
Jan 11 2017
```On Thursday, 12 January 2017 at 00:30:33 UTC, Ignacious wrote:
On Wednesday, 11 January 2017 at 19:23:10 UTC, Razvan Nitu
wrote:
[...]

If you change the return type to a void* your code basically
works.

void* makeMultidimensionalArray(T, Allocator)(auto ref
Allocator alloc, size_t[] lengths)
{
if (lengths.length == 1)
{
int x = 0x01FEEF01;
return cast(void*)makeArray!T(alloc, lengths, x);
}
else
{
alias E = typeof(makeMultidimensionalArray!T(alloc,
lengths[1..\$]));

auto ret = makeArray!E(alloc, lengths);
foreach (ref e; ret)
e = makeMultidimensionalArray!T(alloc,
lengths[1..\$]);
return cast(void*)ret;
}
}

The problem is that then you need to cast back and that
essentially results in the original problem. Can be done but
probably gonna have to use string mixins.

That isn't a solution and will probably introduce other bugs.

As soon as you have to result to using void* is an indication of
```
Jan 12 2017    thedeemon <dlang thedeemon.com> writes:
```On Wednesday, 11 January 2017 at 19:23:10 UTC, Razvan Nitu wrote:
Hi,

I am currently trying to create a function
makeMultidimensionalArray which allocates memory for a
multidimensional array.

You were very close to the answer:

auto makeMultidimensionalArray(int N, T, Allocator)(auto ref
Allocator alloc, size_t[N] lengths)
{
static if (lengths.length == 1)
{
return makeArray!T(alloc, lengths);
}
else
{
alias E = typeof(makeMultidimensionalArray!(N-1,T)(alloc,
lengths[1..\$]));
auto ret = makeArray!E(alloc, lengths);
foreach (ref e; ret)
e = makeMultidimensionalArray!(N-1, T)(alloc,
lengths[1..\$]);
return ret;
}
}

The key point is that return type depends on length of "lengths"
array (dimensionality), so if this length is only known at
runtime this is a dependent type, something D lacks (you'll need
Idris or Agda for those). In D return type must be known at
compile time and hence the length of "lengths" must be a compile
time argument. With such argument everything compiles smoothly.
```
Jan 12 2017