Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std.typecons.asArray #9967

Open
dlangBugzillaToGithub opened this issue Apr 3, 2013 · 3 comments
Open

std.typecons.asArray #9967

dlangBugzillaToGithub opened this issue Apr 3, 2013 · 3 comments

Comments

@dlangBugzillaToGithub
Copy link

bearophile_hugs reported this on 2013-04-03T19:44:52Z

Transfered from https://issues.dlang.org/show_bug.cgi?id=9871

CC List

  • greensunny12

Description

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]);
}
// - - - - - - - - - - - - - - - -
@dlangBugzillaToGithub
Copy link
Author

bearophile_hugs commented on 2013-04-03T21:15:04Z

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]);
}

@dlangBugzillaToGithub
Copy link
Author

bearophile_hugs commented on 2013-04-04T20:10:52Z

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

@dlangBugzillaToGithub
Copy link
Author

greensunny12 commented on 2018-02-10T23:24:05Z

> A simpler implementation:

Well, it's just:

---
tup.expand.only;
---


https://run.dlang.io/is/vJaBtM

I'm not so sure whether this will be added to Tuple, but as Tuples will hopefully soon be part of the language, they should be first-class range citizens too.

@LightBender LightBender removed the P4 label Dec 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants