Thread overview
Re: What to use instead of array.join if RHS is not a range?
Nov 27, 2012
H. S. Teoh
Nov 27, 2012
Andrej Mitrovic
Nov 27, 2012
Jonathan M Davis
Nov 27, 2012
Andrej Mitrovic
Nov 27, 2012
Jonathan M Davis
Nov 27, 2012
Andrej Mitrovic
Nov 27, 2012
Jonathan M Davis
Nov 28, 2012
jerro
November 27, 2012
On Tue, Nov 27, 2012 at 02:33:00AM +0100, Andrej Mitrovic wrote:
> This is what I want:
> 
> struct S { int x; }
> 
> void main()
> {
>     S[] arr = [S(2), S(4), S(6)];
>     S s = S(0);
>     arr.join(s);  // fails here
>     assert(arr == [S(2), S(0), S(4), S(0), S(6)]);
> }
> 
> Calling join like that fails, specifically it fails because "s" is not a forward range (I don't know why it's implemented like that..). Is there some other function which can join with an element as a separator?

What about std.algorithm.joiner?


T

-- 
All problems are easy in retrospect.
November 27, 2012
On 11/27/12, H. S. Teoh <hsteoh@quickfur.ath.cx> wrote:
> What about std.algorithm.joiner?

Same problem.

Anyway here's a quick lazy (pun intended) implementation:

import std.array;
import std.stdio;

struct MyJoiner(T)
{
    T[] array;
    T sep;

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

    void popFront()
    {
        if (!useSep)
            array.popFront();

        useSep ^= 1;
    }

    bool useSep;
    @property T front()
    {
        if (useSep)
            return sep;
        else
            return array.front;
    }
}

auto myJoiner(T)(T[] array, T sep)
{
    return MyJoiner!T(array, sep);
}

struct S { int x; }

void main()
{
    S[] arr = [S(2), S(4), S(6)];
    S s = S(0);
    auto range = arr.myJoiner(s);
    assert(array(range) == [S(2), S(0), S(4), S(0), S(6)]);
}
November 27, 2012
On Tuesday, November 27, 2012 02:33:00 Andrej Mitrovic wrote:
> This is what I want:
> 
> struct S { int x; }
> 
> void main()
> {
>     S[] arr = [S(2), S(4), S(6)];
>     S s = S(0);
>     arr.join(s);  // fails here
>     assert(arr == [S(2), S(0), S(4), S(0), S(6)]);
> }
> 
> Calling join like that fails, specifically it fails because "s" is not a forward range (I don't know why it's implemented like that..). Is there some other function which can join with an element as a separator?

All you need to do is put it in an array.

arr.join([s]);

should work just fine. Or, if you don't want to allocate on the heap, you could do

S[1] s = void;
s[0] = S(0);
arr.join(s[]);

Granted, it _is_ a bit odd that join doesn't accept an element (and I don't think that it's the only range-based function with this problem), but it's easy enough to turn a single element into a range if you need to.

- Jonathan M Davis
November 27, 2012
On 11/27/12, Jonathan M Davis <jmdavisProg@gmx.com> wrote:
> All you need to do is put it in an array.
>
> arr.join([s]);

Still doesn't work.

> S[1] s = void;
> s[0] = S(0);
> arr.join(s[]);

Neither does that.

http://d.puremagic.com/issues/show_bug.cgi?id=9082
November 27, 2012
On Tuesday, November 27, 2012 13:05:12 Andrej Mitrovic wrote:
> On 11/27/12, Jonathan M Davis <jmdavisProg@gmx.com> wrote:
> > All you need to do is put it in an array.
> > 
> > arr.join([s]);
> 
> Still doesn't work.
> 
> > S[1] s = void;
> > s[0] = S(0);
> > arr.join(s[]);
> 
> Neither does that.
> 
> http://d.puremagic.com/issues/show_bug.cgi?id=9082

Oh, wait. The problem is that you're calling join on something that isn't a range of ranges. It makes no sense to join an array which doesn't hold ranges. There's nothing to join. Rather, you're asking it to insert an element between every element in range, which is similar but not the same.

- Jonathan M Davis
November 27, 2012
On 11/27/12, Jonathan M Davis <jmdavisProg@gmx.com> wrote:
> Rather, you're asking it to insert an element
> between
> every element in range, which is similar but not the same.

In that case, which function to use? Because it already behaves in this way for strings.
November 27, 2012
On Tuesday, November 27, 2012 23:35:55 Andrej Mitrovic wrote:
> On 11/27/12, Jonathan M Davis <jmdavisProg@gmx.com> wrote:
> > Rather, you're asking it to insert an element
> > between
> > every element in range, which is similar but not the same.
> 
> In that case, which function to use? Because it already behaves in this way for strings.

But it doesn't. It requires a range of ranges, and a string is a range of dchar. You need a range of strings to use join on strings.

assert(join(["hello", "silly", "world"], " ") == "hello silly world");

will work.

assert(join("hello world", " ") == "h e l l o w o r l d");

won't. You seem to be looking for a function which will insert an element between every element in a range rather than one that joins ranges, and I'm not aware of any function in Phobos which will do that. There may be a way to get one to do what you want, but I can't think of how at the moment.

- Jonathan M Davis
November 28, 2012
> won't. You seem to be looking for a function which will insert an element
> between every element in a range rather than one that joins ranges, and I'm
> not aware of any function in Phobos which will do that. There may be a way to
> get one to do what you want, but I can't think of how at the moment.

roundRobin can be used for that. I think this is suposed to work:

auto r = roundRobin(arr, repeat(s).take(arr.length - 1));

This currently gives this error:

 Error: template instance std.range.roundRobin!(S[],Take!(Repeat!(S))) error instantiating

I think this is due to the fact that roundRobin.front has multiple return statements, some of which return an lvalue and some of which return an rvalue when instantiated with one range with rvalue elements and one with lvalue elements. The return type of roundRobin is auto ref and it seems that this becomes ref in this case. Because I don't know exactly how auto ref is supposed to work, I don't know whether this is a bug in DMD or in roundRobin.front.

This works:

auto r = roundRobin(arr, repeat(s).take(arr.length - 1).array);

And so does this:

auto r2 = roundRobin(arr.map!"a", repeat(s).take(arr.length - 1));