Thread overview
error with std.range.put - readN
May 04, 2015
Baz
May 04, 2015
sclytrack
May 04, 2015
Jonathan M Davis
May 04, 2015
Ali Çehreli
May 04, 2015
Jonathan M Davis
May 04, 2015
the following program fails because of the `put` function :

---
import std.stdio;
import std.range;

size_t readN(T, Range)(ref Range src, ref Range dst, size_t n)
if (isInputRange!Range && isOutputRange!(Range, T))
{
    size_t result;

    while(1)
    {
        if (src.empty || result == n)
            break;

        put(dst, src.front()); // here
        src.popFront;

        ++result;
    }


    return result;
}

void main(string[] args)
{
    int[] src = [1,2,3];
    int[] dst = [0];

    auto n = readN!int(src, dst, 2);

    writeln(dst);
}
---

If i replace `put` by a cat op (`~`) it works, however the cat op only works here because i test the template with two int[].

What's wrong ?
May 04, 2015
On Monday, 4 May 2015 at 14:33:23 UTC, Baz wrote:
> the following program fails because of the `put` function :
>
> ---
> import std.stdio;
> import std.range;
>
> size_t readN(T, Range)(ref Range src, ref Range dst, size_t n)
> if (isInputRange!Range && isOutputRange!(Range, T))
> {
>     size_t result;
>
>     while(1)
>     {
>         if (src.empty || result == n)
>             break;
>
>         put(dst, src.front()); // here
>         src.popFront;
>
>         ++result;
>     }
>
>
>     return result;
> }
>
> void main(string[] args)
> {
>     int[] src = [1,2,3];
>     int[] dst = [0];
>
>     auto n = readN!int(src, dst, 2);
>
>     writeln(dst);
> }
> ---
>
> If i replace `put` by a cat op (`~`) it works, however the cat op only works here because i test the template with two int[].
>
> What's wrong ?

I believe the put(R,E) calls the doPut(R, E)


private void doPut(R, E)(ref R r, auto ref E e)
{
//...
 else static if (isInputRange!R)
{
   static assert(is(typeof(r.front = e)),
        "Cannot nativaly put a " ~ E.stringof ~ " into a " ~ R.stringof ~ ".");
   r.front = e;
   r.popFront();
}
//...
}


So basically you put elements into the list until it is empty.


import std.stdio;
import std.range;

void main()
{
   int [] list = new int [10];
   writeln(list);    //10 zeros
   list.front = 5;   //1 five and 9 zeroes
   writeln(list);
   list.popFront();  //9 zeroes left.
   writeln(list);
}







May 04, 2015
On Monday, 4 May 2015 at 14:33:23 UTC, Baz wrote:
> the following program fails because of the `put` function :
>
> ---
> import std.stdio;
> import std.range;
>
> size_t readN(T, Range)(ref Range src, ref Range dst, size_t n)
> if (isInputRange!Range && isOutputRange!(Range, T))
> {
>     size_t result;
>
>     while(1)
>     {
>         if (src.empty || result == n)
>             break;
>
>         put(dst, src.front()); // here
>         src.popFront;
>
>         ++result;
>     }
>
>
>     return result;
> }
>
> void main(string[] args)
> {
>     int[] src = [1,2,3];
>     int[] dst = [0];
>
>     auto n = readN!int(src, dst, 2);
>
>     writeln(dst);
> }
> ---
>
> If i replace `put` by a cat op (`~`) it works, however the cat op only works here because i test the template with two int[].
>
> What's wrong ?

Your destination is too small. When arrays are treated as output ranges, they get written to like a buffer. They don't get appended to. If you want to append to them, then use Appender for the output range.

On a side note, it's very backwards that you're calling front with parens and popFront without. front is usually a property function (in which case, if @property ever gets sorted out properly, front() wouldn't compile), and popFront is never a property function (so you _can_ call it without parens, but it's kind of weird to do so).

- Jonathan M Davis
May 04, 2015
On 05/04/2015 07:33 AM, Baz wrote:

>      int[] src = [1,2,3];
>      int[] dst = [0];

In addition to what others said, even if dst had room for two elements, it would lose the newly added element due to a popFront() called implicitly during put()'ting.

    int[] dst = [0, 0];    // <-- Has room now

    auto n = readN!int(src, dst, 2);

    writeln(dst);          // <-- Prints "[]" WAT?

One solution is to introduce another slice that would not be popFront'ed:

    int[] dst = [0, 0];
    int[] dst2 = dst;    // <-- This

    auto n = readN!int(src, dst, 2);

    writeln(dst2);       // <-- prints "[1, 2]"

However, Appender is a better solution.

Ali

May 04, 2015
On Monday, 4 May 2015 at 18:50:45 UTC, Ali Çehreli wrote:
> On 05/04/2015 07:33 AM, Baz wrote:
>
> >      int[] src = [1,2,3];
> >      int[] dst = [0];
>
> In addition to what others said, even if dst had room for two elements, it would lose the newly added element due to a popFront() called implicitly during put()'ting.
>
>     int[] dst = [0, 0];    // <-- Has room now
>
>     auto n = readN!int(src, dst, 2);
>
>     writeln(dst);          // <-- Prints "[]" WAT?
>
> One solution is to introduce another slice that would not be popFront'ed:
>
>     int[] dst = [0, 0];
>     int[] dst2 = dst;    // <-- This
>
>     auto n = readN!int(src, dst, 2);
>
>     writeln(dst2);       // <-- prints "[1, 2]"
>
> However, Appender is a better solution.

I really think that output ranges need to be rethought through. In most cases, lazy input ranges should be used instead, but they do have their uses. The problem is that they're not designed well enough or thoroughly enough to be very useful outside of Appender. Using arrays as output ranges is particularly bad, but the fact that there is no way to know whether an output range even has room to put a new element or range of elements onto its end makes output ranges unusable in many cases IMHO.

- Jonathan M Davis