Jump to page: 1 2
Thread overview
Why tuples are not ranges?
Jun 28, 2018
Mr.Bingo
Jun 28, 2018
Timoses
Jun 28, 2018
Alex
Jun 28, 2018
Mr.Bingo
Jun 28, 2018
Ali Çehreli
Jun 28, 2018
Mr.Bingo
Jun 28, 2018
Ali Çehreli
Jun 29, 2018
Alex
Jun 29, 2018
Jonathan M Davis
Jun 29, 2018
Ali Çehreli
Jun 30, 2018
Ali Çehreli
Jun 28, 2018
Meta
June 28, 2018
Seems like it would unify things quite a bit.

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

void main()
{
	auto t = tuple(3,4,5,6);
	//auto t = [3,4,5,6];
	
	writeln(t.map!(a => 3*a).sum());
	
	
}
June 28, 2018
On Thursday, 28 June 2018 at 14:35:33 UTC, Mr.Bingo wrote:
> Seems like it would unify things quite a bit.
>
> import std.typecons, std.range, std.array, std.algorithm, std.stdio;
>
> void main()
> {
> 	auto t = tuple(3,4,5,6);
> 	//auto t = [3,4,5,6];
> 	
> 	writeln(t.map!(a => 3*a).sum());
> 	
> 	
> }

You could make a range out of tuples. This seems to work:

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

    void main()
    {
        auto t = tuple(3,4,5,6);
        auto m = t.expand.only;
        writeln(m.map!(a => 3*a).sum());
    }

Tuple.expand will expand the elements when calling a function (in this case only from std.range).
https://dlang.org/library/std/typecons/tuple.expand.html
June 28, 2018
On Thursday, 28 June 2018 at 14:35:33 UTC, Mr.Bingo wrote:
> Seems like it would unify things quite a bit.

Yeah... this is, because you can't popFront on a tuple, as the amount of entries is fixed. You can, however, popFront on every range.

But as Timoses wrote you can easily make a range out of a tuple, for example by slicing:

´´´
import std.typecons, std.range, std.array, std.algorithm, std.stdio;

    void main()
    {
        auto t = tuple(3,4,5,6);
		auto m = [t[]];
        writeln(m.map!(a => 3*a).sum());
    }
´´´
June 28, 2018
On Thursday, 28 June 2018 at 16:02:59 UTC, Alex wrote:
> On Thursday, 28 June 2018 at 14:35:33 UTC, Mr.Bingo wrote:
>> Seems like it would unify things quite a bit.
>
> Yeah... this is, because you can't popFront on a tuple, as the amount of entries is fixed. You can, however, popFront on every range.
>
> But as Timoses wrote you can easily make a range out of a tuple, for example by slicing:
>
> ´´´
> import std.typecons, std.range, std.array, std.algorithm, std.stdio;
>
>     void main()
>     {
>         auto t = tuple(3,4,5,6);
> 		auto m = [t[]];
>         writeln(m.map!(a => 3*a).sum());
>     }
> ´´´

But is this going to be optimized?

BTW, surely the tuple has a popFront! Just pop the last element and returns a new tuple.

That is, a tuple is a range! Just because it doesn't implement the proper functions doesn't mean it can't do so.

It is clearly easy to see if a tuple is empty, to get the front, and to pop the front and return a new tuple with n - 1 elements, which is really just the tuple(a sliced tuple, say) with the first member hidden.

Since a tuple is fixed at compile time you can "virtually" pop the elements, it doesn't mean that a tuple is not a range.

So, maybe for it to work the compiler needs to be able to slice tuples efficiently(not convert to dynamic arrays).

This is moot if the compiler can realize that it can do most of the work at compile time but I'm not so sure it can.

I mean, if you think about it, the memory layout of a tuple is sequential types:

T1
T2
...

So, to popFront a tuple is just changing the starting offset.

June 28, 2018
On 06/28/2018 10:00 AM, Mr.Bingo wrote:

> But is this going to be optimized?

Not our job! :o)

> That is, a tuple is a range!

Similar to the array-slice distinction, tuple is a container, needing its range.

> It is clearly easy to see if a tuple is empty, to get the front,

Ok.

> and to
> pop the front and return a new tuple with n - 1 elements, which is
> really just the tuple(a sliced tuple, say) with the first member hidden.

That would be special for tuples because popFront does not return a new range (and definitely not with a new type) but mutates the existing one.

Here is a quick and dirty library solution:

// TODO: Template constraints
auto rangified(T)(T t) {
    import std.traits : CommonType;
    import std.conv : to;

    alias ElementType = CommonType!(T.tupleof);

    struct Range {
        size_t i;

        bool empty() {
            return i >= t.length;
        }

        ElementType front() {
            final switch (i) {
                static foreach (j; 0 .. t.length) {
                case j:
                    return t[j].to!ElementType;
                }
            }
        }

        void popFront() {
            ++i;
        }

        enum length = t.length;

        // TODO: save(), opIndex(), etc.
    }

    return Range();
}

unittest {
    import std.typecons : tuple;
    import std.stdio : writefln;
    import std.range : ElementType;

    auto t = tuple(5, 3.5, false);
    auto r = t.rangified;

    writefln("%s elements of '%s': %(%s, %)",
             r.length, ElementType!(typeof(r)).stringof, r);
}

void main() {
}

Prints

3 elements of 'double': 5, 3.5, 0

Ali

June 28, 2018
On Thursday, 28 June 2018 at 18:03:09 UTC, Ali Çehreli wrote:
> On 06/28/2018 10:00 AM, Mr.Bingo wrote:
>
> > But is this going to be optimized?
>
> Not our job! :o)
>
> > That is, a tuple is a range!
>
> Similar to the array-slice distinction, tuple is a container, needing its range.
>
> > It is clearly easy to see if a tuple is empty, to get the
> front,
>
> Ok.
>
> > and to
> > pop the front and return a new tuple with n - 1 elements,
> which is
> > really just the tuple(a sliced tuple, say) with the first
> member hidden.
>
> That would be special for tuples because popFront does not return a new range (and definitely not with a new type) but mutates the existing one.
>
> Here is a quick and dirty library solution:
>
> // TODO: Template constraints
> auto rangified(T)(T t) {
>     import std.traits : CommonType;
>     import std.conv : to;
>
>     alias ElementType = CommonType!(T.tupleof);
>
>     struct Range {
>         size_t i;
>
>         bool empty() {
>             return i >= t.length;
>         }
>
>         ElementType front() {
>             final switch (i) {
>                 static foreach (j; 0 .. t.length) {
>                 case j:
>                     return t[j].to!ElementType;
>                 }
>             }
>         }
>
>         void popFront() {
>             ++i;
>         }
>
>         enum length = t.length;
>
>         // TODO: save(), opIndex(), etc.
>     }
>
>     return Range();
> }
>
> unittest {
>     import std.typecons : tuple;
>     import std.stdio : writefln;
>     import std.range : ElementType;
>
>     auto t = tuple(5, 3.5, false);
>     auto r = t.rangified;
>
>     writefln("%s elements of '%s': %(%s, %)",
>              r.length, ElementType!(typeof(r)).stringof, r);
> }
>
> void main() {
> }
>
> Prints
>
> 3 elements of 'double': 5, 3.5, 0
>
> Ali

Thanks, why not add the ability to pass through ranges and arrays and add it to phobos?
June 28, 2018
On Thursday, 28 June 2018 at 17:00:37 UTC, Mr.Bingo wrote:
> I mean, if you think about it, the memory layout of a tuple is sequential types:
>
> T1
> T2
> ...
>
> So, to popFront a tuple is just changing the starting offset.

You're right; it can definitely be done.

struct TupleRange(T...)
{
        size_t index;
        Tuple!T store;

        @property length()
        {
            assert(index <= store.length);
            return store.length - index;
        }

        Algebraic!T front()
        {
            assert(length > 0);
            return typeof(return)(store[index]);
        }

        void popFront()
        {
            assert(length > 0);
            index++;
        }
}
June 28, 2018
On 06/28/2018 11:08 AM, Mr.Bingo wrote:

> Thanks, why not add the ability to pass through ranges and arrays and
> add it to phobos?

Makes sense. It needs an enhancement request at http://issues.dlang.org/, a good implementation, and a pull request. :)

Ali

June 29, 2018
On Thursday, 28 June 2018 at 19:02:51 UTC, Ali Çehreli wrote:
> On 06/28/2018 11:08 AM, Mr.Bingo wrote:
>
> > Thanks, why not add the ability to pass through ranges and
> arrays and
> > add it to phobos?
>
> Makes sense. It needs an enhancement request at http://issues.dlang.org/, a good implementation, and a pull request. :)
>
> Ali

Wouldn't this be weird from the semantic view?

Assume, I define a tuple with named fields "key" and "value", where a popFront is defined.
Now, I use popFront.
Is the "key" field still available? Why? Or, why not?
What happens, if I name a field of a tuple "front"? Is it available after using popFront? And where does it point to?

I mean, I use something similar in my own project, but doesn't one has to make a clear distinction between a container and a slice of this container? Or should static arrays be unified in the same manner?
June 29, 2018
On Friday, June 29, 2018 05:52:03 Alex via Digitalmars-d-learn wrote:
> On Thursday, 28 June 2018 at 19:02:51 UTC, Ali Çehreli wrote:
> > On 06/28/2018 11:08 AM, Mr.Bingo wrote:
> > > Thanks, why not add the ability to pass through ranges and
> >
> > arrays and
> >
> > > add it to phobos?
> >
> > Makes sense. It needs an enhancement request at http://issues.dlang.org/, a good implementation, and a pull request. :)
> >
> > Ali
>
> Wouldn't this be weird from the semantic view?
>
> Assume, I define a tuple with named fields "key" and "value",
> where a popFront is defined.
> Now, I use popFront.
> Is the "key" field still available? Why? Or, why not?
> What happens, if I name a field of a tuple "front"? Is it
> available after using popFront? And where does it point to?
>
> I mean, I use something similar in my own project, but doesn't one has to make a clear distinction between a container and a slice of this container? Or should static arrays be unified in the same manner?

It wouldn't make any sense to turn a Tuple into a range. However, if all of the values are of the same type, it might make sense to create a range from each of the values in the Tuple. Just like static arrays, they're indexable, so there's a clear order to them. So, I suppose that Tuple could be made sliceable, or a helper function could be created that takes a Tuple and returns a range which wraps it.

However, Tuples frequently have different types for each of their members (in which case, creating a range from a Tuple makes no sense at all), and really, a Tuple is essentially a way to declare a POD struct of values without explicitly declaring a struct. And at that point, talking about getting a range over a Tuple is basically the same thing as talking about creating a range from an arbitrary struct whose members all happen to have the same type - and that would be pretty weird.

So, under limited circumstances, a range could be constructed from a Tuple, but in general, it just strikes me as a bizarre thing to do. Tuples and ranges are very distinct concepts, and Tuples aren't really containers.

- Jonathan M Davis


« First   ‹ Prev
1 2