www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Polymorphic ranges?

reply Andrej Mitrovic <none none.none> writes:
As an example I have a cyclic buffer (using std.range.Cycle) where I can set
the lower and upper bounds of the buffer. I'd also like to enable a stepping
mode, so I thought first about using std.range.Stride.

The code: http://codepad.org/TR7NDWTC

This line is commented out:
//~ buffer = stride(buffer, newStep);

Obviously I can't assign a Stride structure to a Cycle structure. Structures
aren't polymorphic.

The idea was that the private _buffer could be traversed in different ways, and
the Work structure would allow reconfiguration on how the public "buffer" walks
through the private _buffer array, this would be done at runtime via function
calls like "setStep" which changes the buffer type.

But this isn't possible since buffer can only be one type, since it's a
structure.

So the question is, how can I use std.range and its various types 
polymorphically, is that in any way possible?

From what I can tell std.range functions all return structs. I was hoping of
being able to do something like:

class Work
{
    float[256] _buffer;
    InfiniteRange buffer;

    this() { buffer = new InfiniteRange(_buffer); }  // initialize

    void setStride()
    {
        buffer = new Stride(buffer);  // now buffer has a dynamic type of
Stride, which would be a subtype of InfiniteRange
    }
}

And then main would create a Work object, and call its buffer.front and
buffer.popFront properties, and later call setStride to change how the object
behaves by simply creating a new subtype which has the same interface but
different behavior. 

But I can't do that here since pretty much everything in std.range returns a
struct.
Apr 30 2011
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 01.05.2011 6:33, Andrej Mitrovic wrote:
 As an example I have a cyclic buffer (using std.range.Cycle) where I can set
the lower and upper bounds of the buffer. I'd also like to enable a stepping
mode, so I thought first about using std.range.Stride.

 The code: http://codepad.org/TR7NDWTC

 This line is commented out:
 //~ buffer = stride(buffer, newStep);

 Obviously I can't assign a Stride structure to a Cycle structure. Structures
aren't polymorphic.

 The idea was that the private _buffer could be traversed in different ways,
and the Work structure would allow reconfiguration on how the public "buffer"
walks through the private _buffer array, this would be done at runtime via
function calls like "setStep" which changes the buffer type.

 But this isn't possible since buffer can only be one type, since it's a
structure.

 So the question is, how can I use std.range and its various types 
polymorphically, is that in any way possible?

  From what I can tell std.range functions all return structs. I was hoping of
being able to do something like:

 class Work
 {
      float[256] _buffer;
      InfiniteRange buffer;

      this() { buffer = new InfiniteRange(_buffer); }  // initialize

      void setStride()
      {
          buffer = new Stride(buffer);  // now buffer has a dynamic type of
Stride, which would be a subtype of InfiniteRange
      }
 }

 And then main would create a Work object, and call its buffer.front and
buffer.popFront properties, and later call setStride to change how the object
behaves by simply creating a new subtype which has the same interface but
different behavior.

 But I can't do that here since pretty much everything in std.range returns a
struct.
There is a polymorphic wrapper for any range in std.range, try inputRangeObject & outputRangeObject. Every such range object is derived from the most suitable interface InputRange, ForwardRange etc. -- Dmitry Olshansky
May 01 2011
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I'm not sure how to use those wrappers though. Maybe I'm just doing it wrong:

http://codepad.org/eHIdhasc

But it seems these wrappers have some problems, the docs say about the
interfaces:

Limitations:
These interfaces are not capable of forwarding ref access to elements.
Infiniteness of the wrapped range is not propagated.
Length is not propagated in the case of non-random access ranges.
May 01 2011
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 01.05.2011 18:30, Andrej Mitrovic wrote:
 I'm not sure how to use those wrappers though. Maybe I'm just doing it wrong:

 http://codepad.org/eHIdhasc

 But it seems these wrappers have some problems, the docs say about the
 interfaces:

 Limitations:
 These interfaces are not capable of forwarding ref access to elements.
 Infiniteness of the wrapped range is not propagated.
 Length is not propagated in the case of non-random access ranges.
Well, this compiles, you just need to pick suitable type of range 'interface', that's the subtle thingie: http://codepad.org/uE0nIwbk Limitations are caused by bug, that is going to get fixed eventually ;) -- Dmitry Olshansky
May 01 2011
next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Yeah it seems a common interface is what I should have used. I've
tried it in numerous ways but I got the interface type wrong
apparently. Thanks Dmitry.

Here's a quick example:
http://codepad.org/RhNiUHU2

I hope those bugs get squashed so I can have more fun with these ranges. :)
May 01 2011
parent reply =?ISO-8859-1?Q?Ali_=C7ehreli?= <acehreli yahoo.com> writes:
On 05/01/2011 09:09 AM, Andrej Mitrovic wrote:
 Yeah it seems a common interface is what I should have used. I've
 tried it in numerous ways but I got the interface type wrong
 apparently. Thanks Dmitry.

 Here's a quick example:
 http://codepad.org/RhNiUHU2
To make it more convenient to others, I paste Andrej Mitrovic's code: module cyclicBuffer; import std.stdio; import std.range; import core.thread; import std.random; enum BufferSize = 10; struct Work { private float[BufferSize] _buffer; InputRange!(float) buffer; this(T)(T unused) { _buffer[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; buffer = inputRangeObject(cycle(_buffer[])); } void setNewRange(size_t min, size_t max) { buffer = inputRangeObject(cycle(_buffer[min..max])); } void setStep(size_t newStep) { buffer = inputRangeObject(stride(buffer, newStep)); } } void main() { writeln(); auto work = Work(1); size_t count; int min; int max; size_t step; while (!work.buffer.empty) { write(work.buffer.front); stdout.flush(); work.buffer.popFront; if (++count == 20) { min = uniform(0, BufferSize / 2); max = uniform(BufferSize / 2, BufferSize); step = uniform(1, 5); work.setNewRange(min, max); work.setStep(step); writefln("\nmin: %s, max: %s, step: %s", min, max-1, step); count = 0; } Thread.sleep( dur!("msecs")(100) ); } }
 I hope those bugs get squashed so I can have more fun with these ranges. :)
Ali
May 01 2011
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
The reason I've posted them online is because they snippets are not
that short. And it's much easier to read them from a
syntax-highlighted website than from a plaintext newsgroup reader.
May 01 2011
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
s/they/the
May 01 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Unfortunately as you can see I can't convert a Stride to a
RandomAccessInfinite object, so I've had to use an InputRange
interface (I can also use ForwardRange which is more derived). I'm not
sure why I can't use it (maybe it's that bug again?), because Stride
itself does offer random access.
May 01 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Actually that design of mine was bad because it creates a long chain
of virtual calls since setStep just wraps the buffer object and
creates a new object every time.

Here's an alternate implementation: https://gist.github.com/950647
May 01 2011
prev sibling next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Btw,

 * Limitations:
 *
 * These interfaces are not capable of forwarding $(D ref) access to elements.

Why not? I can use auto ref to make e.g. an input range interface that
can return ref elements:

http://codepad.org/kmenIDk7

Why aren't the interfaces defined like that in std.range?
May 01 2011
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 01.05.2011 21:40, Andrej Mitrovic wrote:
 Btw,

   * Limitations:
   *
   * These interfaces are not capable of forwarding $(D ref) access to elements.

 Why not? I can use auto ref to make e.g. an input range interface that
 can return ref elements:

 http://codepad.org/kmenIDk7

 Why aren't the interfaces defined like that in std.range?
One reason might be is that it was implemented under worse compiler condition ;) Seems worthy of a bug report, or better yet a pull request. -- Dmitry Olshansky
May 01 2011
parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
opSlice isn't supported either. Gah.., so much for this being useful.
I'll have to roll my own.

Now I know why Tango was developed. <g>
May 01 2011
prev sibling parent =?ISO-8859-1?Q?Ali_=C7ehreli?= <acehreli yahoo.com> writes:
On 05/01/2011 08:04 AM, Dmitry Olshansky wrote:
 On 01.05.2011 18:30, Andrej Mitrovic wrote:
 I'm not sure how to use those wrappers though. Maybe I'm just doing it
 wrong:

 http://codepad.org/eHIdhasc

 But it seems these wrappers have some problems, the docs say about the
 interfaces:

 Limitations:
 These interfaces are not capable of forwarding ref access to elements.
 Infiniteness of the wrapped range is not propagated.
 Length is not propagated in the case of non-random access ranges.
Well, this compiles, you just need to pick suitable type of range 'interface', that's the subtle thingie: http://codepad.org/uE0nIwbk
To make it more convenient to other, I paste Dmitry Olshansky's code: import std.stdio; import std.range; import core.thread; import std.random; enum BufferSize = 10; struct Work { private float[BufferSize] _buffer; RandomAccessInfinite!(float) buffer; this(T)(T unused) { _buffer[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; buffer = inputRangeObject(cycle(_buffer[])); } void setNewRange(size_t min, size_t max) { buffer = inputRangeObject(cycle(_buffer[min..max])); } } void main(){} I also paste the compilation output: Line 6: enum declaration is invalid Line 6: Declaration expected, not '=' Line 13: semicolon expected following function declaration Line 13: Declaration expected, not '(' Line 17: no identifier for declarator buffer Line 24: unrecognized declaration
 Limitations are caused by bug, that is going to get fixed eventually ;)
Ali
May 01 2011
prev sibling parent =?ISO-8859-1?Q?Ali_=C7ehreli?= <acehreli yahoo.com> writes:
On 05/01/2011 07:30 AM, Andrej Mitrovic wrote:
 I'm not sure how to use those wrappers though. Maybe I'm just doing it wrong:

 http://codepad.org/eHIdhasc
To make it more convenient to others, I paste Andrej Mitrovic's code: import std.stdio; import std.range; import core.thread; import std.random; enum BufferSize = 10; struct Work { private float[BufferSize] _buffer; InputRangeObject!(float[]) buffer; this(T)(T unused) { _buffer[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; buffer = inputRangeObject(cycle(_buffer[])); } void setNewRange(size_t min, size_t max) { buffer = inputRangeObject(cycle(_buffer[min..max])); } } void main(){} /* Error: cannot implicitly convert expression (inputRangeObject(cycle(this._buffer[min..max]))) of type std.range.InputRangeObject!(Cycle!(float[])).InputRangeObject to std.range.InputRangeObject!(float[]).InputRangeObject */
 But it seems these wrappers have some problems, the docs say about the
 interfaces:

 Limitations:
 These interfaces are not capable of forwarding ref access to elements.
 Infiniteness of the wrapped range is not propagated.
 Length is not propagated in the case of non-random access ranges.
Ali
May 01 2011