June 02, 2010
On Wed, Jun 2, 2010 at 21:41, Andrei Alexandrescu < SeeWebsiteForEmail@erdani.org> wrote:

> On 06/02/2010 02:29 PM, Philippe Sigaud wrote:
>
>>
>>
>> On Wed, Jun 2, 2010 at 19:57, bearophile <bearophileHUGS@lycos.com <mailto:bearophileHUGS@lycos.com>> wrote:
>>
>>    Philippe Sigaud:
>>     > What, do you also need the no-arg version of iota?
>>     >
>>     > :-p
>>
>>    I'd like a generator (range) similar to the Python itertools.count,
>>    that yields numbers starting from the given number (defaulting to
>>    zero) and just goes on and on. You can use it in many situations:
>>    http://docs.python.org/library/itertools.html#itertools.count
>>
>>
>> Yes, it's handy. It's one of the first range I made, a year ago.
>>
>
> iota(n, n.max) is close. Well, it's not infinite, but cycle(iota(n, n.max))
> is. Probably a version using BigInt would be most sensible.
>



As it's mostly used to enumerate/index ranges or generate some simple numbers, iota(n,n.max) is largely good enough, except, as you said it's not infinite. I never used iota much, it regularly crashed on me in the beginning. I can't remember why.

I don't know if your suggestion of BigInt is a joke or not, but the truth is, I have a version using any numerical type, I called it numberz. numbers is the standard version, numberz!T the templated. It uses T for indexing also (it's a random-access range), which allowed me to do

auto r = numberz(BigInt("1000000000"),  BigInt("10000000000"));
auto n = r[BigInt("3000000000")];


Hmm, this doesn't work:
auto i = iota(BigInt("0"),BigInt("10"), BigInt("1"));

But honestly, I just used it to learn ranges and D, and never had a real use
for it. It's just the kind of things that's useful to try, to break out of
some preconceptions (using a size_t to index..., 1u as a step).
As you said, it already takes loooots of iterations to exhaust a long.


 Philippe


June 03, 2010
Andrei Alexandrescu:
> iota(n, n.max) is close. Well, it's not infinite, but cycle(iota(n, n.max)) is. Probably a version using BigInt would be most sensible.


An enumerate() too can be useful (not much tested yet):


import std.range: isForwardRange, hasLength, isBidirectionalRange, ElementType;
import std.typecons: Tuple;
import std.array: front, back, empty, popFront, popBack;

/// ...
struct Enumerate(R) if (isForwardRange!R) {
    alias Tuple!(size_t, "index", ElementType!R, "item") TPair;
    R _range;
    size_t _index;
    static if (isBidirectionalRange!R && hasLength!R)
        immutable size_t _originalEnd;

    this(R input) {
        _range = input;
        static if (isBidirectionalRange!R && hasLength!R)
            _originalEnd = _range.length - 1;

    }

    /// Range primitive implementations.
    ref TPair front() {
        return TPair(_index, _range.front());
    }

    /// Ditto
    bool empty() {
        return _range.empty();
    }

    /// Ditto
    void popFront() {
        _range.popFront();
        _index++;
    }

    static if (isBidirectionalRange!R && hasLength!R) {
        /// Ditto
        ref TPair back() {
            return TPair(_originalEnd + _index, _range.back());
        }
    }

    static if (isBidirectionalRange!R && hasLength!R) {
        /// Ditto
        void popBack() {
            _range.popBack();
            _index--;
        }
    }

    static if (hasLength!R) {
        /// Ditto
        @property auto length() {
            return _range.length;
        }
    }
}

/// Ditto
Enumerate!R enumerate(R)(R input) if (isForwardRange!R) {
    return Enumerate!R(input);
}

import std.stdio: write, writeln;

void main() {
    string[] arr = ["y", "n", "y", "n", "y"];

    foreach (el; enumerate(arr))
        write(el, " ");
    writeln("\n");

    foreach_reverse (el; enumerate(arr))
        write(el, " ");
    writeln("\n");
}


I don't know if a reverse iteration on an enumerate can be useful.

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

I have used that to try to implement in D2 this Python code:

>>> arr = ["y", "n", "y", "n", "y"]
>>> [i for i,el in enumerate(arr) if el == "y"]
[0, 2, 4]


This is a basic D version, Appender not used:

import std.stdio: writeln;

void main() {
    // input data
    string[] arr = ["y", "n", "y", "n", "y"];

    //
    int[] indexes;
    foreach (i, item; arr)
        if (item == "y")
            indexes ~= i;
    writeln(indexes);
    writeln();
}

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

This is a more functional quite bad-looking D2 version, that doesn't work (see http://d.puremagic.com/issues/show_bug.cgi?id=4264 ):

import std.stdio: writeln;
import std.algorithm: filter, array, map;

void main() {
    // input data
    string[] arr = ["y", "n", "y", "n", "y"];

    auto r1 = filter!((p){ return p.item == "y"; })(enumerate(arr));
    auto r2 = map!((p){ return p.index; })(r1);
    writeln(array(r2));
}

Bye,
bearophile
June 03, 2010
On 06/02/2010 07:24 PM, bearophile wrote:
> Andrei Alexandrescu:
>> iota(n, n.max) is close. Well, it's not infinite, but cycle(iota(n,
>> n.max)) is. Probably a version using BigInt would be most sensible.
>
>
> An enumerate() too can be useful (not much tested yet):

No need to write enumerate() - it's zip(iota(0, size_t.max), r).

Andrei
June 03, 2010
Andrei Alexandrescu:
> No need to write enumerate() - it's zip(iota(0, size_t.max), r).

What does it happen if someone gives you the sequence produced by:
zip(r, iota(0, size_t.max))
?
You have can require an adaptor because it's not the standard convention.
While enumerate() always gives you indexes at the first place. And the index/item fields have a standard name that you remember in your code.

Finding the minimal number of pieces to do something is useful, but sometimes if an operation is common enough it can be useful to give it a new name. It's called chunking.

Inside an expression that can contain other stuff do you prefer to use:
zip(iota(0, size_t.max), items)
or
enumerate(items)
?
The second makes the expression shorter and more readable in its purpose.

Bye,
bearophile
June 03, 2010
On 06/02/2010 08:04 PM, bearophile wrote:
> Andrei Alexandrescu:
>> No need to write enumerate() - it's zip(iota(0, size_t.max), r).
>
> What does it happen if someone gives you the sequence produced by:
> zip(r, iota(0, size_t.max))
> ?
> You have can require an adaptor because it's not the standard convention.
> While enumerate() always gives you indexes at the first place. And the index/item fields have a standard name that you remember in your code.
>
> Finding the minimal number of pieces to do something is useful, but sometimes if an operation is common enough it can be useful to give it a new name. It's called chunking.
>
> Inside an expression that can contain other stuff do you prefer to use:
> zip(iota(0, size_t.max), items)
> or
> enumerate(items)
> ?
> The second makes the expression shorter and more readable in its purpose.
>
> Bye,
> bearophile

My point was that there's no need to sit down and implement functionality. Just write

auto enumerate(R)(R r) if (isInputRange!R)
{
    return zip(iota(0, size_t.max), r);
}


Andrei
June 03, 2010
Andrei Alexandrescu:

> My point was that there's no need to sit down and implement functionality. Just write
> 
> auto enumerate(R)(R r) if (isInputRange!R)
> {
>      return zip(iota(0, size_t.max), r);
> }

Right, thank you. A std lib can contain even small functions/HOFs (that are the result of combination of few other things), if they are very commonly useful.

Bye,
bearophile
June 03, 2010
On Wed, 02 Jun 2010 20:21:49 -0500, Andrei Alexandrescu wrote:
> 
> My point was that there's no need to sit down and implement functionality. Just write
> 
> auto enumerate(R)(R r) if (isInputRange!R) {
>      return zip(iota(0, size_t.max), r);
> }

Should this work with v2.043? I get an error if I try to enumerate an array, for example:

    /* example.d */
    import std.range;

    auto enumerate(R)(R r) if (isInputRange!R) {
         return zip(iota(0, size_t.max), r);
    }

    void main() {
      auto e = enumerate([10,20,30]);
    }

$ dmd example.d
/usr/include/d/dmd/phobos/std/range.d(1773): Error: cannot implicitly
convert expression (&this.ranges._field_field_0.front) of type uint
delegate() to uint*

(The error message is unfortunate: it doesn't indicate the offending
expression in example.d.)

Thanks,
Graham
June 03, 2010
On 06/03/2010 10:01 AM, Graham Fawcett wrote:
>    import std.range;
>
>      auto enumerate(R)(R r) if (isInputRange!R) {
>           return zip(iota(0, size_t.max), r);
>      }
>
>      void main() {
>        auto e = enumerate([10,20,30]);
>      }

I cry bug.

Andrei
June 03, 2010
On Thu, 03 Jun 2010 10:15:33 -0500, Andrei Alexandrescu wrote:

> On 06/03/2010 10:01 AM, Graham Fawcett wrote:
>>    import std.range;
>>
>>      auto enumerate(R)(R r) if (isInputRange!R) {
>>           return zip(iota(0, size_t.max), r);
>>      }
>>
>>      void main() {
>>        auto e = enumerate([10,20,30]);
>>      }
> 
> I cry bug.

LOL! Andrei, you are a very terse guy. :)

Do you cry a bug in my example, in std.range, or D 2.043?

Graham

June 03, 2010
On 06/03/2010 10:25 AM, Graham Fawcett wrote:
> On Thu, 03 Jun 2010 10:15:33 -0500, Andrei Alexandrescu wrote:
>
>> On 06/03/2010 10:01 AM, Graham Fawcett wrote:
>>>     import std.range;
>>>
>>>       auto enumerate(R)(R r) if (isInputRange!R) {
>>>            return zip(iota(0, size_t.max), r);
>>>       }
>>>
>>>       void main() {
>>>         auto e = enumerate([10,20,30]);
>>>       }
>>
>> I cry bug.
>
> LOL! Andrei, you are a very terse guy. :)
>
> Do you cry a bug in my example, in std.range, or D 2.043?

My code sucks.

Andrei