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() {
}

Reply via email to