I implemented a simple separatorless joiner as follows:

auto joiner(RoR)(RoR r)
if (isInputRange!RoR && isInputRange!(ElementType!RoR))
{
    static struct Result
    {
    private:
        RoR _items;
        ElementType!RoR _current;
        void prime()
        {
            for (;; _items.popFront())
            {
                if (_items.empty) return;
                if (!_items.front.empty) break;
            }
            _current = _items.front;
            _items.popFront();
        }
    public:
        this(RoR r)
        {
            _items = r;
            prime();
        }
        @property auto empty()
        {
            return _current.empty;
        }
        @property auto ref front()
        {
            assert(!empty);
            return _current.front;
        }
        void popFront()
        {
            assert(!_current.empty);
            _current.popFront();
            if (_current.empty) prime();
        }
        static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
        {
            @property auto save()
            {
                Result copy;
                copy._items = _items.save;
                copy._current = _current.save;
                return copy;
            }
        }
    }
    return Result(r);
}

The code has a few properties that I'd like to discuss a bit:

1. It doesn't provide bidirectional primitives, although it often could. The rationale is that implementing back and popBack incurs size and time overheads that are difficult to justify. Most of the time people just want to join stuff and go through it forward. The counterargument is that providing those primitives would make join more interesting and opens the door to other idioms. What say you?

2. joiner uses an idiom that I've experimented with in the past: it defines a local struct and returns it. As such, joiner's type is impossible to express without auto. I find that idiom interesting for many reasons, among which the simplest is that the code is terse, compact, and doesn't pollute the namespace. I'm thinking we should do the same for Appender - it doesn't make much sense to create an Appender except by calling the appender() function.

3. Walter, Don, kindly please fix ddoc so it works with auto. What used to be an annoyance becomes a disabler for the idiom above. Currently it is impossible to document joiner (aside from unsavory tricks).

4. I found the prime() idiom quite frequent when defining ranges. Essentially prime() positions the troops by the border. Both the constructor and popFront() call prime().

5. auto and auto ref rock - they allow simple, correct definitions.

6. Currently joiner() has a bug: it will not work as expected for certain ranges. Which ranges are those, and how can the bug be fixed?


Andrei

Reply via email to