Thread overview
Issue Turning Template into Variadic Template
Mar 30, 2016
jmh530
Mar 30, 2016
H. S. Teoh
Mar 30, 2016
jmh530
Mar 30, 2016
jmh530
Mar 31, 2016
H. S. Teoh
Mar 31, 2016
Artur Skawina
Mar 31, 2016
jmh530
Mar 31, 2016
ag0aep6g
Mar 31, 2016
H. S. Teoh
March 30, 2016
I wrote a version of cartesianProduct that will return the cartesian product when the some of the types are not ranges. The original code is below.

My issue is that I can't figure out how to turn it into a variadic template. The latest thing I tried is:

auto mixedCartesianProduct(T...)(T x)
{
	import std.algorithm : cartesianProduct;
	
	foreach(t; x)
        	t = conditionalOnly(t);
	
	return cartesianProduct(x);
}

but this doesn't work because it changes the type of x. I don't really want to change the type of x. I just want to be able to pass the changed versions to cartesianProduct as in the original code below.

map won't work for the same reasons.

//Original:
auto conditionalOnly(T)(T x)
{
	import std.range : isInputRange;
	
	static if (isInputRange!T)
		return x;
	else
	{
		import std.range : only;
		return only(x);
	}
}

auto mixedCartesianProduct(T, U)(T x, U y)
{
	import std.algorithm : cartesianProduct;
	
	return cartesianProduct(conditionalOnly(x), conditionalOnly(y));
}

void main()
{
	import std.stdio : writeln;
	import std.range : iota;

	auto a = 1;
	auto b = iota(2);
	
	auto c = mixedCartesianProduct(a, b);
	writeln(c);
}
March 30, 2016
On Wed, Mar 30, 2016 at 06:12:40PM +0000, jmh530 via Digitalmars-d-learn wrote:
> I wrote a version of cartesianProduct that will return the cartesian product when the some of the types are not ranges. The original code is below.
> 
> My issue is that I can't figure out how to turn it into a variadic template.  The latest thing I tried is:
[...]

Does this do what you want?

	import std.algorithm.setops : cartesianProduct;
	import std.range : only;
	import std.range.primitives;
	import std.meta : AliasSeq;

	template ImplType(T...)
	{
		static if (T.length == 0)
			alias ImplType = AliasSeq!();
		else
		{
			static if (isInputRange!(T[0]))
				alias FirstType = T[0];
			else
				alias FirstType = typeof(only(T[0].init));
			alias ImplType = AliasSeq!(FirstType, ImplType!(T[1 .. $]));
		}
	}

	auto conditionalOnly(alias fun, T...)(T x) {
		ImplType!T y;
		foreach (i, e; x) {
			static if (isInputRange!(typeof(e)))
				y[i] = e;
			else
				y[i] = only(e);
		}
		return fun(y);
	}

	void main() {
		import std.stdio;
		writeln(conditionalOnly!cartesianProduct(1, [2, 3], 4, [5, 6]));
	}

Note that conditionalOnly isn't specifically tied to cartesianProduct; you can use it on anything that receives a variadic number of range arguments.


T

-- 
"I'm not childish; I'm just in touch with the child within!" - RL
March 30, 2016
On Wednesday, 30 March 2016 at 18:56:29 UTC, H. S. Teoh wrote:
>
> Does this do what you want?
>

I think it does. That's an approach I would not have thought of. I do not really know much about AliasSeq.

March 30, 2016
On Wednesday, 30 March 2016 at 18:56:29 UTC, H. S. Teoh wrote:
>
> Does this do what you want?
>

Okay, I've looked at this a bit more thoroughly and it works perfectly (perhaps with a better name put in phobos?).

If I'm understanding this correctly, the ImplType creates the correct type signature for the output and then conditionalOnly puts the right value in.

One thing that is confusing is that when I create a new function that adjusts conditionalOnly to just return y instead of fun(y), then I get an error about returning a tuple. I'm like, where did I use a tuple. I guess related to
https://issues.dlang.org/show_bug.cgi?id=15436
March 30, 2016
On Wed, Mar 30, 2016 at 08:43:03PM +0000, jmh530 via Digitalmars-d-learn wrote:
> On Wednesday, 30 March 2016 at 18:56:29 UTC, H. S. Teoh wrote:
> >
> >Does this do what you want?
> >
> 
> Okay, I've looked at this a bit more thoroughly and it works perfectly (perhaps with a better name put in phobos?).
> 
> If I'm understanding this correctly, the ImplType creates the correct type signature for the output and then conditionalOnly puts the right value in.

Yes.

It was a quick hack, though, and involves assigning the ranges to a local variable.  There ought to be a way to pass the arguments directly, while substituting the non-ranges with only(x).  But that would probably involve even more black magic that I'm already invoking. :-P  It's pretty close to the point where I'd just throw up my hands and say, forget the template black magic shenanigans, just write a mixin and call it a day.


> One thing that is confusing is that when I create a new function that adjusts conditionalOnly to just return y instead of fun(y), then I get an error about returning a tuple. I'm like, where did I use a tuple. I guess related to https://issues.dlang.org/show_bug.cgi?id=15436

Yes... basically the compiler is complaining that it doesn't know how to return a value with of AliasSeq type.  Why it can't do this, is a complex question that I don't have the time to get into right now... but basically, if you want to return it instead of calling the target function, you have to wrap it in a std.typecons.Tuple struct (how's that for confusing terminology?!), and use .expand to unwrap it when you need to pass it to a function.


T

-- 
Always remember that you are unique. Just like everybody else. -- despair.com
March 31, 2016
On 03/30/16 20:12, jmh530 via Digitalmars-d-learn wrote:
> I wrote a version of cartesianProduct that will return the cartesian product when the some of the types are not ranges. The original code is below.
> 
> My issue is that I can't figure out how to turn it into a variadic template.

> auto mixedCartesianProduct(T, U)(T x, U y)
> {
>     import std.algorithm : cartesianProduct;
> 
>     return cartesianProduct(conditionalOnly(x), conditionalOnly(y));
> }

   auto mixedCartesianProduct(T...)(T x)
   {
       import std.range, std.algorithm : cartesianProduct;

       return mixin(`cartesianProduct(`~iota(T.length).map!`"conditionalOnly(x["~text(a)~"])"`().join(",")~`)`);
   }

artur
March 31, 2016
On Thursday, 31 March 2016 at 10:27:41 UTC, Artur Skawina wrote:
>
>    auto mixedCartesianProduct(T...)(T x)
>    {
>        import std.range, std.algorithm : cartesianProduct;
>
>        return mixin(`cartesianProduct(`~iota(T.length).map!`"conditionalOnly(x["~text(a)~"])"`().join(",")~`)`);
>    }
>
> artur

Thanks, but I try to only use mixins as a last resort.
March 31, 2016
On 30.03.2016 20:12, jmh530 wrote:
> I wrote a version of cartesianProduct that will return the cartesian
> product when the some of the types are not ranges. The original code is
> below.
>
> My issue is that I can't figure out how to turn it into a variadic
> template. The latest thing I tried is:
>
> auto mixedCartesianProduct(T...)(T x)
> {
>      import std.algorithm : cartesianProduct;
>
>      foreach(t; x)
>              t = conditionalOnly(t);
>
>      return cartesianProduct(x);
> }

auto mixedCartesianProduct(T...)(T x)
{
    import std.algorithm : cartesianProduct;
    import std.meta: staticMap;
    import std.traits : ReturnType;

    alias ConditionalOnly(T) = ReturnType!(conditionalOnly!T);
    alias RangeTypes = staticMap!(ConditionalOnly, T);

    RangeTypes ranges;
    foreach (i, t; x) ranges[i] = conditionalOnly(t);

    return cartesianProduct(ranges);
}
March 31, 2016
On Thu, Mar 31, 2016 at 07:43:38PM +0200, ag0aep6g via Digitalmars-d-learn wrote:
> On 30.03.2016 20:12, jmh530 wrote:
> >I wrote a version of cartesianProduct that will return the cartesian product when the some of the types are not ranges. The original code is below.
> >
> >My issue is that I can't figure out how to turn it into a variadic template. The latest thing I tried is:
> >
> >auto mixedCartesianProduct(T...)(T x)
> >{
> >     import std.algorithm : cartesianProduct;
> >
> >     foreach(t; x)
> >             t = conditionalOnly(t);
> >
> >     return cartesianProduct(x);
> >}
> 
> auto mixedCartesianProduct(T...)(T x)
> {
>     import std.algorithm : cartesianProduct;
>     import std.meta: staticMap;
>     import std.traits : ReturnType;
> 
>     alias ConditionalOnly(T) = ReturnType!(conditionalOnly!T);
>     alias RangeTypes = staticMap!(ConditionalOnly, T);
> 
>     RangeTypes ranges;
>     foreach (i, t; x) ranges[i] = conditionalOnly(t);
> 
>     return cartesianProduct(ranges);
> }

+1, very nice. :-)  Much more concise and to-the-point than my attempt. And how did I not remember we have staticMap in Phobos... :-P


T

-- 
Don't get stuck in a closet---wear yourself out.