www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 9871] New: std.typecons.AsRange/asRange

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9871

           Summary: std.typecons.AsRange/asRange
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: Phobos
        AssignedTo: nobody puremagic.com
        ReportedBy: bearophile_hugs eml.cc



In many situations a std.typecons.Tuple has uniform types (this means all its
types are the same). Such tuples are produced by cartesianProduct(), by
std.range.zip(), and so on.

Later I want to process such tuples in various ways, like iterating on them,
using transversal() on them, and so on (in Python code this kind of processing
is common because thanks to dynamic typing tuples are like lists).


If you have one of such uniform tuples like tup:

import std.typecons, std.typetuple;

void main() {
    auto tup = tuple(0, 5, 11, 22);
    static assert(NoDuplicates!(tup.Types).length == 1);
}


You can convert it to a dynamic array like this:

[tup[]]

So now it's usable as a range:

[tup[]].map!(x => x * 2).writeln;

But that syntax is a little noisy, and currently it causes a heap allocation.



This avoids the heap allocation but it's wrong if the tuple is not uniform:

(cast(tup.Types[0]*)&tup[0])[0 .. tup.length]




So I suggest to add asRange/AsRange to std.typecons:


// - - - - - - - - - - - - - - - -
import std.typecons, std.typetuple, std.stdio, std.algorithm,
       std.range;

struct AsRange(T)
if (isTuple!T && NoDuplicates!(T.Types).length == 1) {
    alias Tfield = T.Types[0];
    T inputTuple;
    size_t idx; // This can also be of type Tfield*.

    static this() {
        // Useless?
        foreach (i, _; T.Types)
            static assert(T.tupleof[i].offsetof ==
                          Tfield.sizeof * i);
    }

     property bool empty() { return idx >= T.length; }

     property Tfield front() {
        return (cast(Tfield*)&inputTuple[0])[idx];
    }

    void popFront() { idx++; }

    // This can also be a Random Access Range.
}

// helper function.
AsRange!T asRange(T)(T tup)
if (isTuple!T && NoDuplicates!(T.Types).length == 1) {
    return tup.AsRange!T;
}

void main() { // demo
    auto a = [0];
    auto b = [5];
    auto c = [11];
    auto d = [22];
    Tuple!(int,int,int,int) tup = zip(a, b, c, d).front;
    auto result = tup.asRange.map!(x => x * 2).array;
    assert(result == [0, 10, 22, 44]);
}
// - - - - - - - - - - - - - - - -



In theory simpler code is enough, but because of a current DMD bug (a limit of
alias this), this can't be used with UFCS:


// - - - - - - - - - - - - - - - -
import std.typecons, std.typetuple, std.stdio, std.algorithm,
       std.range;

struct AsRange(T)
if (isTuple!T && NoDuplicates!(T.Types).length == 1) {
    alias Tfield = T.Types[0];
    T inputTuple;

    static this() {
        // Useless?
        foreach (i, _; T.Types)
            static assert(T.tupleof[i].offsetof ==
                          Tfield.sizeof * i);
    }

     property Tfield[] asArray() {
        return (cast(Tfield*)&inputTuple[0])[0 .. T.length];
    }

    alias asArray this;
}

AsRange!T asRange(T)(T tup)
if (isTuple!T && NoDuplicates!(T.Types).length == 1) {
    return tup.AsRange!T;
}

void main() { // demo
    auto a = [0];
    auto b = [5];
    auto c = [11];
    auto d = [22];
    Tuple!(int,int,int,int) tup = zip(a, b, c, d).front;

    // Currently this can't be used:
    // Error: no property 'map' for type 'int[]'
    // tup.asRange.map!(x => x * 2).writeln;

    int[] result = tup.asRange;
    int[] result2 = result.map!(x => x * 2).array;
    assert(result2 == [0, 10, 22, 44]);
}
// - - - - - - - - - - - - - - - -

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Apr 03 2013
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9871


bearophile_hugs eml.cc changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
            Summary|std.typecons.AsRange/asRang |std.typecons.asRange
                   |e                           |



A simpler implementation:


import std.typecons, std.typetuple, std.stdio, std.algorithm,
       std.range;

bool validAsRange(T)() {
    if (!isTuple!T || NoDuplicates!(T.Types).length != 1)
        return false;
    foreach (i, _; T.Types) // Useless?
        if (T.tupleof[i].offsetof != T.Types[0].sizeof * i)
            return false;
    return true;
}

T.Types[0][] asRange(T)(ref T tup) if (validAsRange!T()) {
    return (cast(T.Types[0]*)&tup[0])[0 .. T.length];
}

void main() { // demo
    auto a = [0];
    auto b = [5];
    auto c = [11];
    auto d = [22];
    Tuple!(int,int,int,int) tup = zip(a, b, c, d).front;
    auto result = tup.asRange.map!(x => x * 2).array;
    assert(result == [0, 10, 22, 44]);
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Apr 03 2013
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9871


bearophile_hugs eml.cc changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
            Summary|std.typecons.asRange        |std.typecons.asArray



With the latest version I think "asArray" is a better name.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Apr 04 2013