Thread overview | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
May 14, 2015 Returning an empty range of a given type | ||||
---|---|---|---|---|
| ||||
I've run into this situation a lot: I have a function that returns a range (in this case, a slice of a custom container). In some cases, the function needs to return an empty range. It sounded like takeNone was what I wanted: @nogc auto fun() { return (some_condition) ? getRange() : getRange.takeNone; } but there is a return type ambiguity. I finally ended up doing this: @nogc auto fun() { return (some_condition) ? getRange().take(size_t.max) : getRange.takeNone; } I'm not sure if this is clever or insane. It works, but just looks a bit crazy to me. Does anyone else run into this situation? Have any cool ways to solve it? MyRange is an inputRange, and I can't use a wrapper (InputRange) and keep the @nogc. |
May 14, 2015 Re: Returning an empty range of a given type | ||||
---|---|---|---|---|
| ||||
Posted in reply to rcorre | Actually, this doesn't even seem to work with a custom range: import std.range; import std.stdio; import std.algorithm; struct MyContainer { @nogc auto opSlice() { struct Range { @property bool empty() { return true; } @property int front() { return 9; } void popFront() { } } return Range(); } } /// Return a slice of aa[key], or an empty slice if not found @nogc auto maybeGetRange(MyContainer[string] aa, string key) { alias RangeType = typeof(MyContainer.init[]); auto val = key in aa; return (val is null) ? takeNone!RangeType : (*val)[].take(size_t.max); } Is there any way to create an empty MyContainer.Range() without creating a new container? |
May 14, 2015 Re: Returning an empty range of a given type | ||||
---|---|---|---|---|
| ||||
Posted in reply to rcorre | On Thursday, May 14, 2015 02:47:22 rcorre via Digitalmars-d-learn wrote:
> I've run into this situation a lot:
> I have a function that returns a range (in this case, a slice of
> a custom container).
> In some cases, the function needs to return an empty range.
>
> It sounded like takeNone was what I wanted:
>
> @nogc auto fun() {
> return (some_condition) ? getRange() : getRange.takeNone;
> }
>
> but there is a return type ambiguity. I finally ended up doing this:
>
> @nogc auto fun() {
> return (some_condition) ? getRange().take(size_t.max) :
> getRange.takeNone;
> }
>
> I'm not sure if this is clever or insane.
> It works, but just looks a bit crazy to me.
> Does anyone else run into this situation? Have any cool ways to
> solve it?
> MyRange is an inputRange, and I can't use a wrapper (InputRange)
> and keep the @nogc.
takeNone is as close as you can get in O(1) in the general case, because there's no well-defined way to get an empty range from a range other than to keep calling popFront on it until it's empty (which would be O(n)). So, takeNone gives you an empty range of the same type if it can, and if it can't then the best that it can do is to use takeExactly with 0.
It looks like what you're trying to do is to force the return type to be Take!R in all cases (or the original type if that's what Take!R is able to alias itself to). I don't know if that will work in all cases or not, but I don't see how you have much choice. The only way that you could keep the original range type in all cases would be if we had a standard way to make a range empty that takeNone could use, and we don't.
- Jonathan M Davis
|
May 14, 2015 Re: Returning an empty range of a given type | ||||
---|---|---|---|---|
| ||||
Posted in reply to rcorre | On 05/13/2015 07:47 PM, rcorre wrote: > I've run into this situation a lot: > I have a function that returns a range (in this case, a slice of a > custom container). > In some cases, the function needs to return an empty range. > > It sounded like takeNone was what I wanted: > > @nogc auto fun() { > return (some_condition) ? getRange() : getRange.takeNone; > } > > but there is a return type ambiguity. I finally ended up doing this: > > @nogc auto fun() { > return (some_condition) ? getRange().take(size_t.max) : > getRange.takeNone; > } > > I'm not sure if this is clever or insane. > It works, but just looks a bit crazy to me. > Does anyone else run into this situation? Have any cool ways to solve it? > MyRange is an inputRange, and I can't use a wrapper (InputRange) and > keep the @nogc. I needed the same thing in a code example of this chapter: http://ddili.org/ders/d.en/fibers.html There is this function that returns a range: auto byNode(const(Node) * node) { return new FiberRange!(const(Node)*)( () => nextNode(node)); } I am lucky because although the returned type is opaque to me, I know that it is constructed by a void lambda. So, I could pass (){} to the constructor to make an empty range: auto byNode(const(Tree) tree) { alias RangeType = typeof(byNode(tree.root)); return (tree.root ? byNode(tree.root) : new RangeType(() {})); // ← Empty range } Ali |
May 14, 2015 Re: Returning an empty range of a given type | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | On Thursday, 14 May 2015 at 06:41:45 UTC, Ali Çehreli wrote:
>
> I am lucky because although the returned type is opaque to me, I know that it is constructed by a void lambda.
Yeah, in this case I control the container so I may just add an emptySlice property, but it does seem like it might be a common need (to return an empty range of a given type without having that range constructed beforehand).
|
May 14, 2015 Re: Returning an empty range of a given type | ||||
---|---|---|---|---|
| ||||
Posted in reply to rcorre | So I thought this might work: struct MaybeEmpty(R) if (isInputRange!R) { private bool _isEmpty; private R _input; alias _input this; this(bool isEmpty, R input) { _input = input; _isEmpty = isEmpty; } @property bool empty() { return _isEmpty || _input.empty; } } auto maybeEmpty(R)(bool empty, R input = R.init) if (isInputRange!R) { return MaybeEmpty!R(empty, input); } It's kind of ugly, but it can be used like: auto a = maybeEmpty!MyRange(true); auto b = maybeEmpty!MyRange(false, actualRange); static assert(is(typeof(a) == typeof(b))); However, it fails the input range test: static assert(isInputRange!MyRange); //pass static assert(isInputRange!(typeof(a))); // fail But it seems to be something weird with cross-module template instantiation. If I replace `import std.range` with a copy-paste of `isInputRange`, the above passes. Either that or I'm doing something stupid because I'vebeen staring at this too long. I'm wondering if the isInputRange thing is a bug, so here's a gist if anyone wants to play with it: https://gist.github.com/rcorre/7a62395c53baf3c0bfbc |
May 14, 2015 Re: Returning an empty range of a given type | ||||
---|---|---|---|---|
| ||||
Posted in reply to rcorre | On Thursday, 14 May 2015 at 12:40:57 UTC, rcorre wrote: > So I thought this might work: > > struct MaybeEmpty(R) if (isInputRange!R) { > private bool _isEmpty; > private R _input; > > alias _input this; > > this(bool isEmpty, R input) { > _input = input; > _isEmpty = isEmpty; > } > > @property bool empty() { > return _isEmpty || _input.empty; > } > } > > auto maybeEmpty(R)(bool empty, R input = R.init) if (isInputRange!R) { > return MaybeEmpty!R(empty, input); > } > > It's kind of ugly, but it can be used like: > > auto a = maybeEmpty!MyRange(true); > auto b = maybeEmpty!MyRange(false, actualRange); > static assert(is(typeof(a) == typeof(b))); > > However, it fails the input range test: > > static assert(isInputRange!MyRange); //pass > static assert(isInputRange!(typeof(a))); // fail > > But it seems to be something weird with cross-module template instantiation. If I replace `import std.range` with a copy-paste of `isInputRange`, the above passes. > > Either that or I'm doing something stupid because I'vebeen staring at this too long. > > I'm wondering if the isInputRange thing is a bug, so here's a gist if anyone wants to play with it: > https://gist.github.com/rcorre/7a62395c53baf3c0bfbc How about a more flexible solution? http://dpaste.dzfl.pl/2f99cc270651 |
May 14, 2015 Re: Returning an empty range of a given type | ||||
---|---|---|---|---|
| ||||
Posted in reply to rcorre | On 5/13/15 10:58 PM, rcorre wrote:
> Actually, this doesn't even seem to work with a custom range:
>
> import std.range;
> import std.stdio;
> import std.algorithm;
>
> struct MyContainer {
> @nogc auto opSlice() {
> struct Range {
> @property bool empty() { return true; }
> @property int front() { return 9; }
> void popFront() { }
> }
>
> return Range();
> }
> }
>
> /// Return a slice of aa[key], or an empty slice if not found
> @nogc auto maybeGetRange(MyContainer[string] aa, string key) {
> alias RangeType = typeof(MyContainer.init[]);
> auto val = key in aa;
> return (val is null) ? takeNone!RangeType :
> (*val)[].take(size_t.max);
> }
>
> Is there any way to create an empty MyContainer.Range() without creating
> a new container?
It depends on the guts of MyContainer.Range.
I'm assuming MyContainer.Range has SOME sort of references (i.e. pointers) to the data in the container, so why not just have:
bool empty() { return someRef == null || yourCurrentTest; }
-Steve
|
May 15, 2015 Re: Returning an empty range of a given type | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Thursday, 14 May 2015 at 14:57:26 UTC, Idan Arye wrote: > How about a more flexible solution? > > http://dpaste.dzfl.pl/2f99cc270651 Neat, thanks! On Thursday, 14 May 2015 at 18:44:58 UTC, Steven Schveighoffer wrote: > It depends on the guts of MyContainer.Range. > > I'm assuming MyContainer.Range has SOME sort of references (i.e. pointers) to the data in the container, so why not just have: > > bool empty() { return someRef == null || yourCurrentTest; } > > -Steve In this case, I want to return an empty range when the container instance itself is null. I could have a static method MyContainer.emptySlice, but I feel like I've seen this general situation crop up a lot with small variations. |
May 15, 2015 Re: Returning an empty range of a given type | ||||
---|---|---|---|---|
| ||||
Posted in reply to rcorre | On Friday, 15 May 2015 at 03:22:43 UTC, rcorre wrote:
> On Thursday, 14 May 2015 at 14:57:26 UTC, Idan Arye wrote:
>
>> How about a more flexible solution?
>>
>> http://dpaste.dzfl.pl/2f99cc270651
>
> Neat, thanks!
>
The range I don't pick may be an expression that would fail, so I tweaked it to:
SelectRange!T selectRange(T...)(size_t index, lazy T ranges)
Other than that, it seems to be just what I needed. Thanks again!
|
Copyright © 1999-2021 by the D Language Foundation