On 01/03/2014 09:47 AM, bearophile wrote:

> Unfortunately the D associative arrays specs don't specify this to be
> correct:
>
> zip(aa.byKey, aa.byValue)

I still like that solution. :) Even if it's not spelled out to be correct in the spec, I can't imagine a hash table implementation where byKey and byValue don't iterate in lock step.

I wrote the following Expander range as an exercise. I think it could be useful in Phobos. (I am aware that there are proposals to revamp tuples in Phobos; the following is for 2.064.)

Some issues:

1) Yes, expand may not be the best name as it would be confusing with Tuple.expand, which is a different thing.

2) As with some other InputRanges, the need to call the prime() member function up front in the constructor feels weird. It makes the range one-step eager. However, doing it in the front() conditionaly via 'if (is_primed)' would bring a cost to every call to front().

3) The member rangeFront is needed because Tuple does not have opIndex for dynamic indexes. I can do range.front[0] but I cannot do range.front[currentIndex]. So, my solution was to take advantage of Tuple.expand by wrapping it in a slice, which I have taken out due to performance concern, without ever measuring anything. :p

import std.range;

void main()
{
    auto aa = ["one":"1", "two":"2"];

    assert(zip(aa.byKey, aa.byValue)
           .expand
           .equal([ "one", "1", "two", "2" ]));
}

/* Expands individual members of elements of a tuple-range as elements
 * of this range. */
struct Expander(R)
    if (__traits(compiles, [ ElementType!R ]))
{
    alias ElementT = typeof([ ElementType!R.expand ].front);

    R range;
    ElementT[ElementType!R.length] rangeFront;
    size_t currentIndex;

    this(R range)
    {
        this.range = range;
        prime();
    }

    private void prime()
    {
        if (!empty) {
            currentIndex = 0;

/* The following static foreach "I think" avoids a dynamic array
             * allocation when compared to the following line:
             *
             *   rangeFront = [ range.front.expand ];
             */
            foreach (i, element; range.front) {
                rangeFront[i] = element;
            }
        }
    }

    bool empty() @property
    {
        return range.empty;
    }

    ElementT front() @property
    {
        return rangeFront[currentIndex];
    }

    void popFront()
    {
        ++currentIndex;
        if (currentIndex == ElementType!R.length) {
            range.popFront();
            prime();
        }
    }
}

Expander!R expand(R)(R range)
    if (__traits(compiles, [ ElementType!R ]))
{
    return Expander!R(range);
}

unittest
{
    import std.typecons;

    auto a = [ tuple(1, 2.2), tuple(3, 4.4) ];
    auto expanded = a.expand;
    static assert(is (typeof(expanded.front) == double));
    assert(expanded.equal([ 1, 2.2, 3, 4.4 ]));

    // Incompatible tuple members should fail to compile
    auto mismatched = [ tuple(int.init, string.init) ];
    static assert(!__traits(compiles, mismatched.expand));
}

Ali

Reply via email to