www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Returning an empty range of a given type

reply "rcorre" <ryan rcorre.net> writes:
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 13 2015
next sibling parent reply "rcorre" <ryan rcorre.net> writes:
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 13 2015
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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 14 2015
parent reply "rcorre" <ryan rcorre.net> writes:
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 14 2015
next sibling parent reply "rcorre" <ryan rcorre.net> writes:
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!
May 14 2015
parent "Idan Arye" <GenericNPC gmail.com> writes:
On Friday, 15 May 2015 at 03:47:46 UTC, rcorre wrote:
 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!
Keep in mind that lazy arguments are actually delegates, which means they need the GC. If using the GC doesn't bother you you can use std.range.interfaces to wrap things up in an object: http://dlang.org/phobos/std_range_interfaces.html#InputRange
May 17 2015
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/14/15 11:22 PM, rcorre wrote:
 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; }
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.
I'd have to see the code, but it seems like your situation is different from what I think, or you are not understanding what I'm saying :) As an example: struct MyContainer { private int[] data; struct Range { private int *curData; private int *endOfData; int front() { return *curData; } bool empty() { return curData == endOfData; } void popFront() { ++curData; } } Range opSlice() {return Range(data.ptr, data.ptr + data.length);} } Range.init would be a valid empty range in this case. But I have no idea what your situation is. That's why I said it depends on the guts of the Range. -Steve
May 15 2015
prev sibling next sibling parent Jonathan M Davis via Digitalmars-d-learn writes:
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 13 2015
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
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 13 2015
parent reply "rcorre" <ryan rcorre.net> writes:
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
parent reply "rcorre" <ryan rcorre.net> writes:
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
parent "Idan Arye" <GenericNPC gmail.com> writes:
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