Thread overview
How to implement a range?
Apr 16, 2021
Jack
Apr 16, 2021
Mike Parker
Apr 16, 2021
Ali Çehreli
April 16, 2021

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:

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:

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;
    }
}
April 16, 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.

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.

April 16, 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