www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Issue Turning Template into Variadic Template

reply jmh530 <john.michael.hall gmail.com> writes:
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);
}
Mar 30 2016
next sibling parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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
Mar 30 2016
next sibling parent jmh530 <john.michael.hall gmail.com> writes:
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.
Mar 30 2016
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
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
Mar 30 2016
parent "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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
Mar 30 2016
prev sibling next sibling parent reply Artur Skawina via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
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
Mar 31 2016
parent jmh530 <john.michael.hall gmail.com> writes:
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.
Mar 31 2016
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
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); }
Mar 31 2016
parent "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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.
Mar 31 2016