On Sunday, 14 October 2018 at 15:27:07 UTC, Michael Coulombe wrote:
On Sunday, 14 October 2018 at 14:35:36 UTC, lngns wrote:
On Sunday, 14 October 2018 at 13:18:37 UTC, lngns wrote:
That would require introducing a new type

Or just use int with a negative number... That's how it's done in some dynamic languages. But my point is that it should be compatible with pre-existing code using unsigned indices somehow. I don't think that is possible.

Another way to do this with UFCS:

// write-once library wrapper
struct Indexer(R) {
    R r;
    auto opDollar() { return r.length; }
    auto opIndex(size_t i) { return r[i]; }
}
auto indexer(R)(R r) { return Indexer(r); }

// rewrite index parameter => return wrapped range
auto foo()
{
    auto arr = (...);
    return indexer(arr);
}

// easy-to-use result
auto end = foo[$-1];

Didn't feel like making a full expression tree thing, so feel free to extend this:

struct Index {
    struct Op {
        string op;
        int value;
        void apply(ref size_t val) {
            switch (op) {
                case "+": val += value; break;
                case "-": val -= value; break;
                case "*": val *= value; break;
                case "/": val /= value; break;
                case "%": val %= value; break;
                default: assert(false);
            }
        }
    }
    Op[] ops;
    bool fromEnd;
    this(bool b) { fromEnd = b; }
    this(size_t i) {
        ops ~= Op("+", i);
    }
    static Index opDollar() {
        return Index(true);
    }
    static Index opIndex(Index idx) {
        return idx;
    }
    static Index opIndex(size_t idx) {
        return Index(idx);
    }
    auto opBinary(string op)(int rhs) {
        Index result = this;
        result.ops ~= Op(op, rhs);
        return result;
    }
    auto value(size_t length) {
        size_t result = fromEnd ? length : 0;
        foreach (e; ops)
            e.apply(result);
        return result;
    }
}

struct Indexer(R) {
    R r;
    alias r this;
    Index opDollar() {
        return Index(true);
    }
    auto opIndex(Index idx) {
        return r[idx.value(r.length)];
    }
    auto opIndex(size_t idx) {
        return r[idx];
    }
}

auto indexer(R)(R r) {
    return Indexer!R(r);
}

unittest {
auto a = Index[$-2]; // Will always point to next-to-last element.
    auto arr = [1,2,3,4,5,6,7,8,9].indexer; // Wrap access.
    assert(arr[0] == 1); // Regular access still works.
    assert(arr[a] == 8); // Look ma, I'm using $!
    assert(arr[Index[$-1]] == 9); // Look ma, I'm using $!
}

--
  Simen

Reply via email to