D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 6383 - Unpacking from dynamic array, lazy ranges
Summary: Unpacking from dynamic array, lazy ranges
Status: RESOLVED INVALID
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P2 enhancement
Assignee: No Owner
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-07-26 06:52 UTC by bearophile_hugs
Modified: 2021-01-24 06:41 UTC (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description bearophile_hugs 2011-07-26 06:52:19 UTC
This is a spinoff of Issue 6365  (AutoTupleDeclaration).

Issue 6365 is about syntax sugar to unpack tuples, but unpacking dynamic arrays or lazy ranges too is handy and commonly useful, because in D most arrays are not fixed-sized, and many functions return a lazy range, like lazy splitting, regular expression matches, etc.

The 'lengths don't match' exception is already present in D and I think it's acceptable (when the lengths are statically known there is no need to test lengths at run time, so this exception throwing is not present, so it's nothrow code):

int[] array = [1, 2, 3];
int[2] a2 = array[];

-------------------

Some usage examples, assign from a dynamic array, in D2:

import std.string;
void main() {
    auto s = " foo bar ";
    auto ss = s.split();
    auto sa = ss[0];
    auto sb = ss[1];
}


In Python2.6:

>>> sa, sb = " foo bar ".split()
>>> sa
'foo'
>>> sb
'bar'


Proposed:

import std.string;
void main() {
    auto s = " foo bar ";
    auto (sa, sb) = s.split();
}


-------------------

From lazy range, D2:


import std.algorithm, std.conv, std.string;
void main() {
    string s = " 15 27";

    // splitter doesn't work yet for this
    // (here it doesn't strip the leading space)
    //auto xy = map!(to!int)(splitter(s));

    auto xy = map!(to!int)(split(s));
    int x = xy[0];
    int y = xy[1];
}



In Python:

>>> x, y = map(int, " 15 27".split())
>>> x
15
>>> y
27
>>> from itertools import imap
>>> x, y = imap(int, " 15 27".split())
>>> x
15
>>> y
27


Proposed:

import std.algorithm, std.conv, std.string;
void main() {
    string s = " 15 27";
    (int x, int y) = map!(to!int)(splitter(s));
}

-------------------

From a lazy range, another example. See:
http://rosettacode.org/wiki/Range_expansion#D


D2 code:

import std.stdio, std.regex, std.string, std.conv, std.range;
int[] rangeExpand(string txt) {
    int[] result;
    foreach (r; std.string.split(txt, ",")) {
        auto m = array(match(r, r"^(-?\d+)(-?(-?\d+))?$").captures);
        if (m[2].empty)
            result ~= to!int(m[1]);
        else
            result ~= array(iota(to!int(m[1]), to!int(m[3])+1));
    }
    return result;
}



Python:

import re
def rangeexpand(txt):
    lst = []
    for rng in txt.split(','):
        start,end = re.match('^(-?\d+)(?:-(-?\d+))?$', rng).groups()
        if end:
            lst.extend(xrange(int(start),int(end)+1))
        else:
            lst.append(int(start))
    return lst
print(rangeexpand('-6,-3--1,3-5,7-11,14,15,17-20'))



Proposed (just the important line), from dynamic array:

auto (start,end) = array(match(r, r"^(-?\d+)(-?(-?\d+))?$").captures);


Or better, from the range of regex captures:

auto (start,end) = match(r, r"^(-?\d+)(-?(-?\d+))?$").captures;


Similar unpacking from Python list (that is a dynamic array) or from lazy iterable is quite common in scripting-style Python code.
Comment 1 bearophile_hugs 2011-07-26 06:57:08 UTC
Sorry, this is not correct:

auto (start,end) = array(match(r, r"^(-?\d+)(-?(-?\d+))?$").captures);
auto (start,end) = match(r, r"^(-?\d+)(-?(-?\d+))?$").captures;
Comment 2 Dmitry Olshansky 2011-07-30 08:36:58 UTC
(In reply to comment #1)
> Sorry, this is not correct:
> 
> auto (start,end) = array(match(r, r"^(-?\d+)(-?(-?\d+))?$").captures);
> auto (start,end) = match(r, r"^(-?\d+)(-?(-?\d+))?$").captures;

I'm puzzled, in this case captures contain way more then 2 strings:
0 - whole match
1 - (-?\d+)
2 - (-?(-?\d+))?
3 - (-?\d+)
Comment 3 bearophile_hugs 2011-07-30 08:42:48 UTC
(In reply to comment #2)
> (In reply to comment #1)
> > Sorry, this is not correct:
> > 
> > auto (start,end) = array(match(r, r"^(-?\d+)(-?(-?\d+))?$").captures);
> > auto (start,end) = match(r, r"^(-?\d+)(-?(-?\d+))?$").captures;
> 
> I'm puzzled, in this case captures contain way more then 2 strings:
> 0 - whole match
> 1 - (-?\d+)
> 2 - (-?(-?\d+))?
> 3 - (-?\d+)

What are you puzzled about? I have already said that code I have written is not correct.
Comment 4 Dmitry Olshansky 2011-07-30 08:43:44 UTC
(In reply to comment #3)
> (In reply to comment #2)
> > (In reply to comment #1)
> > > Sorry, this is not correct:
> > > 
> > > auto (start,end) = array(match(r, r"^(-?\d+)(-?(-?\d+))?$").captures);
> > > auto (start,end) = match(r, r"^(-?\d+)(-?(-?\d+))?$").captures;
> > 
> > I'm puzzled, in this case captures contain way more then 2 strings:
> > 0 - whole match
> > 1 - (-?\d+)
> > 2 - (-?(-?\d+))?
> > 3 - (-?\d+)
> 
> What are you puzzled about? I have already said that code I have written is not
> correct.

Ah, don't worry, I supposed you pasted a correction, sily me.
Comment 5 pompei2 2011-12-21 00:38:37 UTC
I think this would be very useful.(In reply to comment #2)
> (In reply to comment #1)
> > Sorry, this is not correct:
> > 
> > auto (start,end) = array(match(r, r"^(-?\d+)(-?(-?\d+))?$").captures);
> > auto (start,end) = match(r, r"^(-?\d+)(-?(-?\d+))?$").captures;
> 
> I'm puzzled, in this case captures contain way more then 2 strings:
> 0 - whole match
> 1 - (-?\d+)
> 2 - (-?(-?\d+))?
> 3 - (-?\d+)

So why couldn't "start" and "end" unpack the whole match and (-?\d+) and ignore the last two?

I totally want to see unpacking in D2.
Comment 6 mhh 2021-01-24 06:41:54 UTC
Not the place + is the subject of a current DIP.