www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Choice ranges?

reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
Today I ran into an interesting situation where I have a function f that
needs to return ranges of different types (but identical element types):

	auto f(A...)(A args) {
		...
		if (someCondition)
			return cartesianProduct(x, y)
				.joiner;
		else
			return cartesianProduct(x, y)
				.joiner
				.filter!someFilter;
	}

This obviously can't compile, because the return types are not the same.
(Note that someCondition is only known at runtime.) But abstractly
speaking, it *should* work, because the element type of the returned
range is identical.

So how would I implement something like this?


T

-- 
The fact that anyone still uses AOL shows that even the presence of options
doesn't stop some people from picking the pessimal one. - Mike Ellis
Mar 28 2014
next sibling parent "Meta" <jared771 gmail.com> writes:
On Friday, 28 March 2014 at 19:02:48 UTC, H. S. Teoh wrote:
 Today I ran into an interesting situation where I have a 
 function f that
 needs to return ranges of different types (but identical 
 element types):

 	auto f(A...)(A args) {
 		...
 		if (someCondition)
 			return cartesianProduct(x, y)
 				.joiner;
 		else
 			return cartesianProduct(x, y)
 				.joiner
 				.filter!someFilter;
 	}

 This obviously can't compile, because the return types are not 
 the same.
 (Note that someCondition is only known at runtime.) But 
 abstractly
 speaking, it *should* work, because the element type of the 
 returned
 range is identical.

 So how would I implement something like this?


 T
You could try using std.variant.Algebraic. I've used it successfully before, but it's a bit clumsy and out of date.
Mar 28 2014
prev sibling next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
H. S. Teoh:

 So how would I implement something like this?
One option is to wrap those ranges in classes. See std.range for the adapters. (I have not used them yet). Bye, bearophile
Mar 28 2014
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/28/2014 01:46 PM, bearophile wrote:
 H. S. Teoh:

 So how would I implement something like this?
One option is to wrap those ranges in classes. See std.range for the adapters. (I have not used them yet).
Link: Short examples here as well under "Run-time polymorphism with inputRangeObject() and outputRangeObject()": http://ddili.org/ders/d.en/ranges_more.html <quote> inputRangeObject() is flexible enough to support all of the non-output ranges: InputRange, ForwardRange, BidirectionalRange, and RandomAccessRange. Because of that flexibility, the object that it returns cannot be defined by auto. </quote> Ali
Mar 28 2014
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03/28/2014 08:00 PM, H. S. Teoh wrote:
 Today I ran into an interesting situation where I have a function f that
 needs to return ranges of different types (but identical element types):

 	auto f(A...)(A args) {
 		...
 		if (someCondition)
 			return cartesianProduct(x, y)
 				.joiner;
 		else
 			return cartesianProduct(x, y)
 				.joiner
 				.filter!someFilter;
 	}

 This obviously can't compile, because the return types are not the same.
 (Note that someCondition is only known at runtime.) But abstractly
 speaking, it *should* work, because the element type of the returned
 range is identical.

 So how would I implement something like this?


 T
The following is as close as I got. I think the definite initialization checks DMD implements are horribly broken for union fields. import std.range, std.algorithm, std.typetuple, std.traits; template CommonElementType(T...)if(allSatisfy!(isInputRange,T)){ alias CommonElementType = CommonType!(staticMap!(ElementType,T)); } private template Neg(alias a){ enum Neg(T...)=!a!T; } struct SumRange(T...)if(allSatisfy!(isInputRange,T)&&!is(void==CommonElementType!T)&&allSatisfy!(Neg!hasElaborateDestructor,T)&&allSatisfy!(Neg!hasElaborateCopyConstructor,T)){ size_t tag; private union{ T rs=void; } // private this(); property front() trusted{ switch(tag){ foreach(i,_;T) case i: return rs[i].front; default: assert(0); } } property bool empty() trusted{ switch(tag){ foreach(i,_;T) case i: return rs[i].empty; default: assert(0); } } void popFront() trusted{ switch(tag){ foreach(i,_;T) case i: return rs[i].popFront(); default: assert(0); } } } private T buildSum(T, size_t tag,S)(S arg) trusted{ T r; r.tag=tag; r.rs[tag]=arg; return r; } auto inl(T,S)(S arg)if(!hasElaborateDestructor!S&&!hasElaborateDestructor!T&&!hasElaborateCopyConstructor!S&&!hasElaborateCopyConstructor!T){ return buildSum!(SumRange!(S,T),0)(arg); } auto inr(S,T)(T arg)if(!hasElaborateDestructor!S&&!hasElaborateDestructor!T&&!hasElaborateCopyConstructor!S&&!hasElaborateCopyConstructor!T){ return buildSum!(SumRange!(S,T),1)(arg); } auto f(R,S)(bool condition,R x,S y){ auto r1(){ return cartesianProduct(x, y).map!(a=>[a.expand]) .joiner; } auto r2(){ return cartesianProduct(x, y).map!(a=>[a.expand]) .joiner .filter!(a=>a>2); } if(condition) return r1().inl!(typeof(r2())); else return r2().inr!(typeof(r1())); } void main(){ import std.stdio; writeln(f(true, [1,2,3], [4,5,6])); writeln(f(false, [1,2,3], [4,5,6])); }
Mar 29 2014
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
 On 03/28/2014 08:00 PM, H. S. Teoh wrote:
Today I ran into an interesting situation where I have a function f
that needs to return ranges of different types (but identical element
types):

	auto f(A...)(A args) {
		...
		if (someCondition)
			return cartesianProduct(x, y)
				.joiner;
		else
			return cartesianProduct(x, y)
				.joiner
				.filter!someFilter;
	}

This obviously can't compile, because the return types are not the
same.  (Note that someCondition is only known at runtime.) But
abstractly speaking, it *should* work, because the element type of
the returned range is identical.

So how would I implement something like this?
[...] Well, eventually I settled on using inputRangeObject() from std.range. While not the most performant (f would have to return InputRangeObject interface, which adds indirection), it was simplest to use and didn't require excessively ugly code. If this part of the code turns out to be a bottleneck, I'll rethink this decision, but for now it works nicely. :) T -- The easy way is the wrong way, and the hard way is the stupid way. Pick one.
Mar 29 2014