Collapsing n-dimensional array to linear (1 dimensional)

Solomon E via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sun Jan 24 18:27:57 PST 2016


On Saturday, 23 January 2016 at 07:57:55 UTC, Ali Çehreli wrote:
>
> auto collapse(R)(R r)
>         if (isArray!R) {
>     return r.joiner.collapse.joiner;
> }
>
> auto collapse(R)(R r)
>         if (!isArray!R) {
>     return r;
> }
>

Ali, that code only passed the one test it had for collapsing a 
three level array. It wouldn't collapse arrays of other numbers 
of levels. It wasn't recursing as appeared to be intended.

Is the following code better D? (I don't know because I'm still 
learning D, so I'd like to be corrected if the comments in my 
code are inaccurate or misleading.)

(See https://issues.dlang.org/show_bug.cgi?id=12062 for where I 
got the idea that `flatten` should be defined to mutate by 
reference. A comment there suggests to use 
std.experimental.ndslice and byElement for that, but ndlslice 
doesn't seem to be in the library anymore.)


import std.stdio;
import std.range;
import std.algorithm;
import std.traits;

/** `collapse` returns an array that can be type inferred
  *      and can be cast to [] without effect
  */

auto collapse(R)(R r)
         if (isArray!(ElementType!R)) {
     return r.joiner.array.collapse;
}

auto collapse(R)(R r)
         if (!isArray!(ElementType!R)) {
     return r;
}

/** `flatten` returns a range Result that can modify the original 
by ref
  *     and .flatten.array is equivalent to .collapse
  */

auto flatten(R)(R r)
         if (isIterable!R && isIterable!(ElementType!R)) {
     return r.joiner.flatten;
}

auto flatten(R)(R r)
         if (!(isIterable!R && isIterable!(ElementType!R))) {
     return r;
}

unittest {
     auto r1 = 3.iota.array;
     auto r2 = 3.iota.map!(i => iota(3 * i, 3 * i + 
3).array).array;
     assert(r2 == [[0,1,2],[3,4,5],[6,7,8]]);
     auto r3 = 3.iota
             .map!(i => iota(3 * i, 3 * i + 3)
                     .map!(j => iota(3 * j, 3 * j + 3)
                             .array)
                     .array)
             .array;
     auto r4 = 3.iota
             .map!(i => iota(3 * i, 3 * i + 3)
                     .map!(j => iota(3 * j, 3 * j + 3)
                             .map!(j => iota(3 * j, 3 * j + 3)
                                     .array)
                             .array)
                     .array)
             .array;
     auto r5 = 3.iota
             .map!(i => iota(3 * i, 3 * i + 3)
                     .map!(j => iota(3 * j, 3 * j + 3)
                             .map!(j => iota(3 * j, 3 * j + 3)
                                     .map!(j => iota(3 * j, 3 * j 
+ 3)
                                             .array)
                                     .array)
                             .array)
                     .array)
             .array;

     writeln("\nOriginal 1 level:\n", r1);
     writeln("Collapsed:\n", r1.collapse);

     writeln("\nOriginal 2 level:\n", r2);
     writeln("Collapsed:\n", r2.collapse);

     writeln("\nOriginal 3 level:\n", r3);
     writeln("Collapsed:\n", r3.collapse);

     writeln("\nOriginal 4 level:\n", r4);
     writeln("Collapsed:\n", r4.collapse);

     writeln("\nOriginal 5 level:\n", r5);
     writeln("Collapsed:\n", r5.collapse);

     // equality (collapse is eager and iota is lazy)
     assert(r1.collapse.equal(iota(3)));
     assert(r2.collapse.equal(iota(9)));
     assert(r3.collapse.equal(iota(27)));
     assert(r4.collapse.equal(iota(81)));
     assert(r5.collapse.equal(iota(243)));

     // value equality
     assert(r1.collapse == iota(3).array);
     assert(r2.collapse == iota(9).array);
     assert(r3.collapse == iota(27).array);
     assert(r4.collapse == iota(81).array);
     assert(r5.collapse == iota(243).array);

     // cast is allowed and does not affect the result
     assert(cast(int[]) r1.collapse == iota(3).array);
     assert(cast(int[]) r2.collapse == iota(9).array);
     assert(cast(int[]) r3.collapse == iota(27).array);
     assert(cast(int[]) r4.collapse == iota(81).array);
     assert(cast(int[]) r5.collapse == iota(243).array);

     // type inference
     auto ar1 = r1.collapse;
     assert(ar1 == iota(3).array);
     auto ar2 = r2.collapse;
     assert(ar2 == iota(9).array);
     auto ar3 = r3.collapse;
     assert(ar3 == iota(27).array);
     auto ar4 = r4.collapse;
     assert(ar4 == iota(81).array);
     auto ar5 = r5.collapse;
     assert(ar5 == iota(243).array);

     // lazy equality
     assert(r1.flatten.equal(iota(3)));
     assert(r2.flatten.equal(iota(9)));
     assert(r3.flatten.equal(iota(27)));
     assert(r4.flatten.equal(iota(81)));
     assert(r5.flatten.equal(iota(243)));

     // equivalence between .flatten.array and .collapse
     assert(r1.flatten.array == ar1);
     assert(r2.flatten.array == ar2);
     assert(r3.flatten.array == ar3);
     assert(r4.flatten.array == ar4);
     assert(r5.flatten.array == ar5);

     // mutation by reference through a flatten
     foreach (ref x; r3.flatten) {
         x++;
     }
     writeln("r3 scalar incremented ", r3);
     auto r3i = iota(1, 4)
             .map!(i => iota(3 * i - 2, 3 * i + 1)
                     .map!(j => iota(3 * j - 2, 3 * j + 1)
                             .array)
                     .array)
             .array;
     assert(r3.equal(r3i));
}

void main() {
}



More information about the Digitalmars-d-learn mailing list