Jump to page: 1 2
Thread overview
how to do this meta-programming? print the address of random element's address of a variable length of arrays?
Sep 12, 2020
mw
Sep 12, 2020
Ali Çehreli
Sep 12, 2020
mw
Sep 12, 2020
Paul Backus
Sep 12, 2020
mw
Sep 12, 2020
Paul Backus
Sep 12, 2020
mw
Sep 12, 2020
Paul Backus
Sep 13, 2020
mw
Sep 13, 2020
mw
Sep 13, 2020
Paul Backus
Sep 13, 2020
mw
Sep 13, 2020
Paul Backus
Sep 13, 2020
H. S. Teoh
September 12, 2020
e.g.

int[] a = new int[la];
int[] b = new int[lb];
int[] c = new int[lc];
int[] d = new int[ld];


the func I want to write, e.g. for 2 arrays (instantiation) is like this:

void print_random_elem_addr(int[] x, int[] y) {
  auto i = random_int_between(0, x.length);
  auto j = random_int_between(0, y.length);
  print(&(x[i], &(y[j]));  // only single print() func call allowed!
}


But I want one generic function, which can be called as:

print_random_elem_addr(a, b);
print_random_elem_addr(a, b, c);
print_random_elem_addr(a, b, c, d);
...


My main question is how to meta-program (generate the parameter list) this line:
  print(&(x[i], &(y[j]));  // only single print() func call allowed!


Thanks!
September 11, 2020
On 9/11/20 6:44 PM, mw wrote:> e.g.
>
> int[] a = new int[la];
> int[] b = new int[lb];
> int[] c = new int[lc];
> int[] d = new int[ld];
>
>
> the func I want to write, e.g. for 2 arrays (instantiation) is like this:
>
> void print_random_elem_addr(int[] x, int[] y) {
>    auto i = random_int_between(0, x.length);
>    auto j = random_int_between(0, y.length);
>    print(&(x[i], &(y[j]));  // only single print() func call allowed!
> }
>
>
> But I want one generic function, which can be called as:
>
> print_random_elem_addr(a, b);
> print_random_elem_addr(a, b, c);
> print_random_elem_addr(a, b, c, d);

If they are all of same type like int[] in this case, then you can variable number of parameters, which means "any number of int[] arrays" below, elements of which can be called either as separate arguments or as a single array argument:

import std.stdio;
import std.random;

void print_random_elem_addr(int[][] arrays...) {
  foreach (i, array; arrays) {
    const chosen = uniform(0, array.length);
    writefln!"Array %s, element %s: %s"(i, chosen, &array[chosen]);
  }
}

void main() {
  auto randomLengthArray() {
    return new int[uniform(1, 101)];
  }

  auto a = randomLengthArray();
  auto b = randomLengthArray();
  auto c = randomLengthArray();

  writeln("As independent arguments:");
  print_random_elem_addr(a, b, c);

  writeln("As a single argument:");
  print_random_elem_addr([a, b, c]);
}

Warning: The array that is automatically generated by the first print_random_elem_addr() call in main() is short-lived: You cannot store a slice of it because the array that contains the arguments may be destroyed upon leaving the function (e.g. in the "independent" case above).

Here is some more information:


http://ddili.org/ders/d.en/parameter_flexibility.html#ix_parameter_flexibility.variadic%20function

There are other ways of doing the same thing. For example, if you want to work with different ranges, you can use tuple template parameters:


http://ddili.org/ders/d.en/templates_more.html#ix_templates_more.tuple%20template%20parameter

Ali


September 12, 2020
On Saturday, 12 September 2020 at 03:11:09 UTC, Ali Çehreli wrote:
> On 9/11/20 6:44 PM, mw wrote:> e.g.
> >
> > int[] a = new int[la];
> > int[] b = new int[lb];
> > int[] c = new int[lc];
> > int[] d = new int[ld];
> >
> >
> > the func I want to write, e.g. for 2 arrays (instantiation)
> is like this:
> >
> > void print_random_elem_addr(int[] x, int[] y) {
> >    auto i = random_int_between(0, x.length);
> >    auto j = random_int_between(0, y.length);
> >    print(&(x[i], &(y[j]));  // only single print() func call
> allowed!
> > }
> >
> >
> > But I want one generic function, which can be called as:
> >
> > print_random_elem_addr(a, b);
> > print_random_elem_addr(a, b, c);
> > print_random_elem_addr(a, b, c, d);

Thanks for the reply.

> If they are all of same type like int[] in this case, then you

but, this is not the intention, we should suppose the array's are heterogeneous type ...

> can variable number of parameters, which means "any number of int[] arrays" below, elements of which can be called either as separate arguments or as a single array argument:
>
> import std.stdio;
> import std.random;
>
> void print_random_elem_addr(int[][] arrays...) {

... to prevent passing in parameters as array of array like this.

>   foreach (i, array; arrays) {
>     const chosen = uniform(0, array.length);
>     writefln!"Array %s, element %s: %s"(i, chosen, &array[chosen]);

actually this writefln will be called n times.

I intentionally require:

  print(&(x[i], &(y[j]));  // only single print() func call allowed!


I.e. I want to learn the generic meta-programming way to assemble such parameter list (&(x[i], &(y[j])) at compile time, it is possible?


September 12, 2020
On Saturday, 12 September 2020 at 03:19:23 UTC, mw wrote:
> I.e. I want to learn the generic meta-programming way to assemble such parameter list (&(x[i], &(y[j])) at compile time, it is possible?

It's possible if you use a helper function. Here's how:

import std.meta: allSatisfy;
import std.traits: isArray;

void printRandomElemAddr(Arrays...)(Arrays arrays)
    if (allSatisfy!(isArray, Arrays))
{
    auto randomElemAddr(size_t i)()
        if (i < arrays.length)
    {
        import std.random: uniform;

        return &arrays[i][uniform(0, $)];
    }

    import std.stdio: writeln;
    import std.meta: staticMap, aliasSeqOf;
    import std.range: iota;

    writeln(staticMap!(randomElemAddr, aliasSeqOf!(iota(arrays.length))));
}

void main()
{
    int[] a = [1];
    int[] b = [2, 3];
    double[] c = [4, 5, 6];

    printRandomElemAddr(a);
    printRandomElemAddr(a, b);
    printRandomElemAddr(a, b, c);
}
September 12, 2020
On Saturday, 12 September 2020 at 14:31:59 UTC, Paul Backus wrote:
> On Saturday, 12 September 2020 at 03:19:23 UTC, mw wrote:
>> I.e. I want to learn the generic meta-programming way to assemble such parameter list (&(x[i], &(y[j])) at compile time, it is possible?
>
> It's possible if you use a helper function. Here's how:
>
> import std.meta: allSatisfy;
> import std.traits: isArray;
>
> void printRandomElemAddr(Arrays...)(Arrays arrays)
>     if (allSatisfy!(isArray, Arrays))
> {
>     auto randomElemAddr(size_t i)()
>         if (i < arrays.length)
>     {
>         import std.random: uniform;
>
>         return &arrays[i][uniform(0, $)];
>     }
>
>     import std.stdio: writeln;
>     import std.meta: staticMap, aliasSeqOf;
>     import std.range: iota;
>
>     writeln(staticMap!(randomElemAddr, aliasSeqOf!(iota(arrays.length))));
> }
>
> void main()
> {
>     int[] a = [1];
>     int[] b = [2, 3];
>     double[] c = [4, 5, 6];
>
>     printRandomElemAddr(a);
>     printRandomElemAddr(a, b);
>     printRandomElemAddr(a, b, c);
> }

Thanks, this works. staticMap and aliasSeqOf is the key.

Now, let me expand this challenge: suppose we need to add a new set of variable length extra parameters in parallel to the arrays, i.e:

     // just use scalar type for demo
     int    extraA;
     string extraB;
     double extraC;

     // need to be called as:
     printRandomElemAddr(extraA, a);
     printRandomElemAddr(extraA, extraB, a, b);
     printRandomElemAddr(extraA, extraB, extraC, a, b, c);


basically, two sets of variadic parameters, but need to be treated differently:

-- the 1st scalar set, just use as it is
-- the 2nd array set, need some processing (which you have done).

Now the question is how to pass & handle 2 sets of variadic parameters?

September 12, 2020
On Saturday, 12 September 2020 at 18:16:51 UTC, mw wrote:
> Now, let me expand this challenge: suppose we need to add a new set of variable length extra parameters in parallel to the arrays, i.e:
>
> [...]
>
> Now the question is how to pass & handle 2 sets of variadic parameters?

void fun(Args...)(Args args)
    if (args.length % 2 == 0)
{
    alias firstSet = args[0 .. $/2];
    alias secondSet = args[$/2 .. $];

    // rest of function body goes here
}

You will probably also want to include some `static asserts` to make sure each set contains arguments of the appropriate types.
September 12, 2020
On Saturday, 12 September 2020 at 19:06:47 UTC, Paul Backus wrote:
> On Saturday, 12 September 2020 at 18:16:51 UTC, mw wrote:
>> Now, let me expand this challenge: suppose we need to add a new set of variable length extra parameters in parallel to the arrays, i.e:
>>
>> [...]
>>
>> Now the question is how to pass & handle 2 sets of variadic parameters?
>
>     alias firstSet = args[0 .. $/2];
>     alias secondSet = args[$/2 .. $];

This solution assumes the two sets are of equal size; what if we don't have such assumption? i.e. we only know the two sets divided into two logical groups.

I've tried something like this: the AliasSeq specify the logical divide

   printRandomElemAddr(AliasSeq!(extraA, extraB, extraC, extraD), a, b, c);

but cannot make it work.

(I'm asking for a more general solution, e.g. what if we have 3, or N sets of variadic parameters?)


Looks like we can only pass 1 variadic parameters, then the question is what's the best way to divide it?

Is there any special marker (variable or type?) can be used to divide? and what's the staticSplit?

September 12, 2020
On Saturday, 12 September 2020 at 19:31:57 UTC, mw wrote:
>
> (I'm asking for a more general solution, e.g. what if we have 3, or N sets of variadic parameters?)
>
>
> Looks like we can only pass 1 variadic parameters, then the question is what's the best way to divide it?
>
> Is there any special marker (variable or type?) can be used to divide? and what's the staticSplit?

It's possible, but there's no widely-used technique, because it's much easier to simply pass each "parameter set" as a separate array or tuple. So your function signature would look like:

auto fun(Set1, Set2, Set3)(Set1 args1, Set2 args2, Set3 args3)
    if (allSatisfy!(Or!(isArray, isTuple), Set1, Set2, Set3))

Or in the variadic case:

auto fun(Args...)(Args args)
    if (allSatisfy!(Or!(isArray, isTuple), Args))

If you have a "real-life" application in mind for this, I'd be curious to hear what it is.
September 13, 2020
On Saturday, 12 September 2020 at 20:29:40 UTC, Paul Backus wrote:
> On Saturday, 12 September 2020 at 19:31:57 UTC, mw wrote:
>>
>> (I'm asking for a more general solution, e.g. what if we have 3, or N sets of variadic parameters?)
>>
>>
>> Looks like we can only pass 1 variadic parameters, then the question is what's the best way to divide it?
>>
>> Is there any special marker (variable or type?) can be used to divide? and what's the staticSplit?
>
> It's possible, but there's no widely-used technique, because it's much easier to simply pass each "parameter set" as a separate array or tuple. So your function signature would look like:
>
> auto fun(Set1, Set2, Set3)(Set1 args1, Set2 args2, Set3 args3)
>     if (allSatisfy!(Or!(isArray, isTuple), Set1, Set2, Set3))
>
> Or in the variadic case:
>
> auto fun(Args...)(Args args)
>     if (allSatisfy!(Or!(isArray, isTuple), Args))
>
> If you have a "real-life" application in mind for this, I'd be curious to hear what it is.

I'm wrapping a C library, trying to write a single D function / template that can work with a group of C functions, hence I need that kind of signature I described.

I'll post the code when I'm ready.

September 12, 2020
On Sat, Sep 12, 2020 at 07:31:57PM +0000, mw via Digitalmars-d-learn wrote: [...]
> I've tried something like this: the AliasSeq specify the logical divide
> 
>    printRandomElemAddr(AliasSeq!(extraA, extraB, extraC, extraD), a, b, c);
> 
> but cannot make it work.

Yes, because AliasSeq auto-expands, so `func(AliasSeq!(A,B,C),
AliasSeq!(D,E,F))` is equivalent to `func(A,B,C,D,E,F)`.


> (I'm asking for a more general solution, e.g. what if we have 3, or N
> sets of variadic parameters?)
> 
> Looks like we can only pass 1 variadic parameters, then the question is what's the best way to divide it?
> 
> Is there any special marker (variable or type?) can be used to divide?
> and what's the staticSplit?

Maybe the nested templates hack could be used? It depends on what exactly you're trying to do though:

	template myFunc(Args1...) {
		template myFunc(Args2...) {
			auto myFunc(/* runtime params here */) {
				// you can use Args1.length and
				// Args2.length here.
				...
			}
		}
	}

	myFunc!(A,B,C)!(D,E,F)(/* runtime args here */);

Alternatively, define a non-eponymous version of AliasSeq that will allow you to pass multiple lists of compile-time parameters without auto-expanding and coalescing them:

	template WrappedSeq(T...) {
		// N.B.: not eponymous
		alias members = T;
	}

	auto myFunc(Args1, Args2)(/*runtime params*/) {
		alias list1 = Args1.members; // need manual unwrap
		alias list2 = Args2.members;
	}

	// (Yeah the caller side will look ugly. C'est la vie.)
	myFunc!(WrappedSeq!(A,B,C), WrappedSeq!(D,E,F))(/*runtime args*/);


T

-- 
Why can't you just be a nonconformist like everyone else? -- YHL
« First   ‹ Prev
1 2