digitalmars.D.learn - How to implement a range?
- Jack (98/98) Apr 15 2021 In order to my array class work with filter, I went to implement
- Mike Parker (39/48) Apr 15 2021 Generally, you don't want your containers to be ranges
- =?UTF-8?Q?Ali_=c3=87ehreli?= (5/6) Apr 16 2021 In case it's useful to others as well, I have two chapters on ranges:
In order to my array class work with filter, I went to implement an ``InputRange``. But I don't quite get how do that and didn't find much help on the docs. From below code, is ``moveFront()`` implemented correctly? I'm using a simple int i as index of current item and in popFront() just increment it. I must reset the i value once the loop is done, right? where am I supposed to do that? opApply()? not properly reseting it result in obvious bugs like subsequent calls doesn't work because the index is in the end of the array: ```d auto arr = new MyArray!int; arr.Add(1); arr.Add(2); arr.Add(3); arr.Add(4); auto r = arr.filter!(n => (n % 2) == 0); auto r2 = arr.filter!(n => n >= 2); writeln(r); // ok writeln(r2); // empty ``` yeah, i'm a bit confused... here's the code: ```d class MyArray(T) : InputRange!T { private T[] arr; private int i = 0; void Add(T item) { arr ~= item; } void Add(T[] items) { foreach(item; items) { Add(item); } } size_t length() nothrow { return arr.length; } bool empty() { return i == length; } T front() { return arr[i]; } void popFront() { i++; } T moveFront() { auto r = front; popFront(); return r; } int opApply(scope int delegate(ref T) dg) { int result = 0; foreach (item; arr) { result = dg(item); if (result) { break; } } return result; } int opApply(scope int delegate(T) dg) { int result = 0; foreach (item; arr) { result = dg(item); if (result) { break; } } return result; } int opApply(scope int delegate(uint, T) dg) { int result = 0; foreach (j, item; arr) { result = dg(j, item); if (result) { break; } } return result; } } ```
Apr 15 2021
On Friday, 16 April 2021 at 06:21:35 UTC, Jack wrote:In order to my array class work with filter, I went to implement an ``InputRange``. But I don't quite get how do that and didn't find much help on the docs. From below code, is ``moveFront()`` implemented correctly? I'm using a simple int i as index of current item and in popFront() just increment it. I must reset the i value once the loop is done, right? where am I supposed to do that? opApply()? not properly reseting it result in obvious bugs like subsequent calls doesn't work because the index is in the end of the array:Generally, you don't want your containers to be ranges themselves. You want them to produce ranges, i.e., separate the duties of iteration from the duties of the container. Also, it's best to make your range types as structs rather than classes for an easier time. A basic input range doesn't need to worry about `moveFront`. So you can get away with `empty`, `front`, and `popFront` on a struct. ```d import std.stdio; class MyArray(T) { private T[] _a; this(T[] a) { _a = a; }; auto opIndex() { return Range(_a[]); } private static struct Range { T[] a; T front() { return a[0]; } void popFront() { a = a[1 .. $]; } bool empty() { return a.length == 0; } } } void main() { auto ma = new MyArray!int([10, 20, 30, 44, 55]); foreach(i; ma[]) { writeln(i); } } ``` I've overloaded the slice operator via the no-arg `opIndex` to provide the range so that you can do `ma[]` to get to it. You'd want to expand on that to handle start & end points for a slice. But anyway, the whole idea behind ranges is you want to keep your iteration separate from the data. Then ranges can be copied around and consumed without every changing the original data structure.
Apr 15 2021
On 4/15/21 11:21 PM, Jack wrote:didn't find much help on the docs.In case it's useful to others as well, I have two chapters on ranges: http://ddili.org/ders/d.en/ranges.html http://ddili.org/ders/d.en/ranges_more.html Ali
Apr 16 2021