www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - chunks equivalent with unpacking?

reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
std.range.chunks is useful for iterating through a range like so:

-----
import std.range;
import std.stdio : writeln;

void main()
{
    float[] arr = [
         0.0,   0.15, 0.0, 1.0,
         0.25, -0.25, 0.0, 1.0,
        -0.25, -0.25, 0.0, 1.0,
    ];

    foreach (xyzw; arr.chunks(4))
    {
        writeln(xyzw[0]);  // write 'x'
    }
}
-----

But instead of having to use indexing for each element in the chunk,
I'd like to use separate arguments instead, like so:

    // nicer API
    foreach (x, y, z, w; arr.chunks(4))
    {
        writeln(x);
    }

Is there a function in Phobos that does this?
Jul 31 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Andrej Mitrovic:

     foreach (x, y, z, w; arr.chunks(4))
     {
         writeln(x);
     }

 Is there a function in Phobos that does this?
Those four variables x y z w influence the type of the foreach, while 4 is a run-time argument. This API can be used: foreach (x, y, z, w; arr.chunks!4) A forward range like that seems easy to implement. Bye, bearophile
Jul 31 2013
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 7/31/13, bearophile <bearophileHUGS lycos.com> wrote:
 Those four variables x y z w influence the type of the foreach,
 while 4 is a run-time argument. This API can be used:

 foreach (x, y, z, w; arr.chunks!4)
Yeah it will have to be CT.
 A forward range like that seems easy to implement.
Can it? I've tried alternatively with opApply but it's problematic to support both ref/non-ref argument versions. I know there's been some sort of automatic unpacking feature recently introduced (in 2.063 maybe?). Got an idea how you'd implement this range?
Jul 31 2013
prev sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 7/31/13, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:
 Can it? I've tried alternatively with opApply but it's problematic to
 support both ref/non-ref argument versions.
Ajh nevermind there's no issue with ref-ness, I just had a bug in my code. So this is easily implementable with opApply but I'm not sure how it can be done with a range type.
Jul 31 2013
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/31/2013 10:58 AM, Andrej Mitrovic wrote:
 On 7/31/13, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:
 Can it? I've tried alternatively with opApply but it's problematic to
 support both ref/non-ref argument versions.
Ajh nevermind there's no issue with ref-ness, I just had a bug in my code. So this is easily implementable with opApply but I'm not sure how it can be done with a range type.
Once the element type is a tuple, 'tuple foreach' will expand them. foreach (x, y, z, w; /* a range that returns tuple of four */) Ali
Jul 31 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Ali Çehreli:

 Once the element type is a tuple, 'tuple foreach' will expand 
 them.

     foreach (x, y, z, w; /* a range that returns tuple of four 
 */)
That part of the D design is not yet formalized, it's a special case, and maybe it will need to be removed :-( It's a significant design mistake introduced recently in D. This is a first nonworking try: import std.range: chunks, Chunks; import std.stdio : writeln; import std.string: format, join; import std.typecons: tuple; import std.typetuple: TypeTuple; template Iota(int stop) { static if (stop <= 0) alias TypeTuple!() Iota; else alias TypeTuple!(Iota!(stop-1), stop-1) Iota; } struct TupleChunks(size_t n, CR) { CR cr; property bool empty() { return cr.empty; } void popFront() { cr.popFront; } property front() { static string gen() { string[] result; foreach (i; Iota!n) result ~= format("cr.front[%d]", i); return result.join(", "); } mixin("return tuple(" ~ gen ~ ");"); } } TupleChunks!(n, Chunks!R) tupleChunks(size_t n, R)(R r) { return typeof(return)(chunks(r, n)); } void main() { float[] arr = [ 0.0, 0.15, 0.0, 1.0, 0.25, -0.25, 0.0, 1.0, -0.25, -0.25, 0.0, 1.0]; foreach (x, y, z, w; arr.tupleChunks!4) x.writeln; } Bye, bearophile
Jul 31 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
 template Iota(int stop) {
     static if (stop <= 0)
         alias TypeTuple!() Iota;
     else
         alias TypeTuple!(Iota!(stop-1), stop-1) Iota;
 }
Silly me, that Iota is not needed: import std.stdio : writeln; import std.range: chunks, Chunks, iota; import std.string: format, join; import std.typecons: tuple; import std.algorithm: map; struct TupleChunks(size_t n, CR) { CR cr; property bool empty() { return cr.empty; } void popFront() { cr.popFront; } property front() { mixin("return tuple(" ~ n.iota.map!(i => format("cr.front[%d]", i)).join(", ") ~ ");"); } } TupleChunks!(n, Chunks!R) tupleChunks(size_t n, R)(R r) { return typeof(return)(chunks(r, n)); } void main() { float[] arr = [ 0.0, 0.15, 0.0, 1.0, 0.25, -0.25, 0.0, 1.0, -0.25, -0.25, 0.0, 1.0]; foreach (x, y, z, w; arr.tupleChunks!4) x.writeln; } (It still doesn't work, but it doesn't work in a better way). Bye, bearophile
Jul 31 2013
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
      property front() {
         mixin("return tuple(" ~
               n.iota.map!(i => format("cr.front[%d]", 
 i)).join(", ") ~
               ");");
     }
That TupleChunks is doing kind of the opposite of this older enhancement request of mine: http://d.puremagic.com/issues/show_bug.cgi?id=9871 Bye, bearophile
Jul 31 2013
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/31/2013 11:19 AM, bearophile wrote:
 template Iota(int stop) {
     static if (stop <= 0)
         alias TypeTuple!() Iota;
     else
         alias TypeTuple!(Iota!(stop-1), stop-1) Iota;
 }
Silly me, that Iota is not needed: import std.stdio : writeln; import std.range: chunks, Chunks, iota; import std.string: format, join; import std.typecons: tuple; import std.algorithm: map; struct TupleChunks(size_t n, CR) { CR cr; property bool empty() { return cr.empty; } void popFront() { cr.popFront; } property front() { mixin("return tuple(" ~ n.iota.map!(i => format("cr.front[%d]", i)).join(", ") ~ ");"); } } TupleChunks!(n, Chunks!R) tupleChunks(size_t n, R)(R r) { return typeof(return)(chunks(r, n)); } void main() { float[] arr = [ 0.0, 0.15, 0.0, 1.0, 0.25, -0.25, 0.0, 1.0, -0.25, -0.25, 0.0, 1.0]; foreach (x, y, z, w; arr.tupleChunks!4) x.writeln; } (It still doesn't work, but it doesn't work in a better way). Bye, bearophile
Fixed magically :) by a line that should have no effect: pragma(msg, ElementType!(typeof(arr.tupleChunks!4))); Of course one must also import ElementType: import std.range: chunks, Chunks, iota, ElementType; Ali
Jul 31 2013
next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/31/2013 11:36 AM, Ali Çehreli wrote:

 Fixed magically :) by a line that should have no effect:

      pragma(msg, ElementType!(typeof(arr.tupleChunks!4)));
And only when inserted before the foreach statement. Ali
Jul 31 2013
prev sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Ali Çehreli:

 Fixed magically :) by a line that should have no effect:

     pragma(msg, ElementType!(typeof(arr.tupleChunks!4)));

 Of course one must also import ElementType:

 import std.range: chunks, Chunks, iota, ElementType;
How nice. Have we just won another bug report? It is worth fixing a small feature that probably should be removed from the language and that I think is not present in D documentation? Bye, bearophile
Jul 31 2013
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 7/31/13, bearophile <bearophileHUGS lycos.com> wrote:
 How nice. Have we just won another bug report?
It's not the only one either, my opApply version works ok alone, but when put into another module with package imports it fails to compile, which is yet another new regression.. It's frustrating having to hit so many regressions lately.
Jul 31 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 7/31/13, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:
 On 7/31/13, bearophile <bearophileHUGS lycos.com> wrote:
 How nice. Have we just won another bug report?
It's not the only one either, my opApply version works ok alone, but when put into another module with package imports it fails to compile, which is yet another new regression.. It's frustrating having to hit so many regressions lately.
Btw my version with opApply: codepad.org/yojenuZl Pasted here for convenience: ----- module test; import std.array; import std.range; import std.stdio; import std.string; /** Implements chunks iteration through an input range. */ struct Pack(Range, size_t count) if (isInputRange!Range) { this(Range range) { this.range = range; } alias Item = ElementType!Range; mixin genPackOpApply!count; private: Range range; } /** Return a struct instance that wraps an input range and provides iteration through $(D count) chunks at a time. */ auto pack(size_t count, Range)(Range range) if (isInputRange!Range) { return Pack!(Range, count)(range); } /// unittest { int[] arr = [ 0, 1, 2, 3, 4, 5, 6, 7 ]; size_t index; foreach (x, y, z, w; arr.pack!4) { if (index++ == 0) assert(x == 0); else assert(x == 4); } foreach (idx, x, y, z, w; arr.pack!4) { if (idx == 0) assert(x == 0); else assert(x == 4); } foreach (idx, ref x, y, z, w; arr.pack!4) { if (idx == 0) x = 1; } assert(arr[0] == 1 && arr[4] == 4); } private mixin template genPackOpApply(size_t count) { mixin(genPackOpApplyImpl(count)); } private string genPackOpApplyImpl(size_t count) { string[] items; foreach (i; 0 .. count) items ~= "items[%s]".format(i); string opApply1 = q{ /// foreach without index int opApply(int delegate(%s) dg) { int result = 0; foreach (items; std.range.chunks(range, %s)) { result = dg(%s); if (result) break; } return result; } }.format(std.array.replicate(["ref Item"], count).join(", "), count, items.join(", ")); string opApply2 = q{ /// foreach with index int opApply(int delegate(size_t index, %s) dg) { int result = 0; size_t index; foreach (items; std.range.chunks(range, %s)) { result = dg(index++, %s); if (result) break; } return result; } }.format(std.array.replicate(["ref Item"], count).join(", "), count, items.join(", ")); return format("%s\n%s", opApply1, opApply2); } void main() { } -----
Jul 31 2013
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 7/31/13, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:
 It's not the only one either, my opApply version works ok alone, but
 when put into another module with package imports it fails to compile,
 which is yet another new regression.. It's frustrating having to hit
 so many regressions lately.
Meanwhile I've reduced it and filed it: http://d.puremagic.com/issues/show_bug.cgi?id=10736
Jul 31 2013