Iterators for D
Sean Kelly
sean at f4.ca
Wed Nov 8 09:54:49 PST 2006
Daniel Keep wrote:
>
> Walter Bright wrote:
>> Sean Kelly wrote:
>>> Since D has slicing, the argument for using iterators to define the
>>> boundaries of a range of randomly accessible elements seems kind of
>>> small to me. ie.
>>>
>>> sort( a.begin, a.begin + 5 ); // sort the first 5 elements
>>> or
>>> sort( a[0 .. 5] );
>>>
>>> I find the second to be cleaner to look at. But I'm undecided whether
>>> we'd lose any power or flexibility by not bothering with random access
>>> iterators.
>> Hmm, instead of 'pointers as iterators', have 'arrays as iterators'.
>> That definitely sounds like a good area to explore.
>
> Hang on; aren't we back to where we are *right now*?
Essentially. But D has no formal description of an iterator type. I
think it would be a useful convention to establish, and doing so
requires determining what types of iterators should be supported, and how.
> Things that need random access override opIndex and opIndexAssign.
>
> Things which you can take a range of values from override opSlice and
> opSliceAssign.
>
> Things that require lock-step iteration override opApply, or supply a
> function that returns a delegate that "runs" a foreach loop.
>
> About the only thing this *doesn't* cover are bi-directional iterators
> (say, iterating back and forth over an infinite series).
This wouldn't be too difficult to do by adding a prev() method and
atBegin() or some such. Or maybe the Java style of hasNext(), next(),
hasPrev(), prev().
> I know some people want to be able to stop iteration and resume it, but
> couldn't that be solved by making opApply return a generator like what
> Python does? Those things are written the same way as a current opApply
> (sans all the return value from the delegate stuff), and can be advanced
> manually.
>
> I know this can be done. I wrote the coroutine module for StackThreads
> a while back, and one of the *very first* things I did was implement
> generic generators. The base generator class essentially just
> implemented opApply switching over to the coroutine every time it needed
> the next value. Here's a port of Python's range() function (well, the
> one and zero argument versions :P):
>
>> class
>> range : Generator!(uint)
>> {
>> // These just let you use range() or range(n)
>> static
>> range
>> opCall()
>> {
>> return range(uint.max);
>> }
>>
>> static
>> range
>> opCall(uint limit)
>> {
>> return new range(limit);
>> }
>>
>> protected:
>> uint limit;
>>
>> // Or you can use new range(n)
>> this(uint limit)
>> {
>> this.limit = limit;
>> super();
>> }
>>
>> // Where the magic happens.
>> override
>> uint
>> run()
>> {
>> uint i = 0;
>> while( i < limit )
>> yield(i++); // yield in D: *very* sexy
>>
>> StopGenerator(); // throws an exception, imitating what
>> // Python does to stop an iterator.
>> }
>> }
>
> Now, from that, we can implement opApply AND next/current in the base
> class. Going with coroutines kills both the opApply and forward
> iterator birds with one stone.
Interesting idea--I'll admit I don't have much experience with Python.
But I think both approaches have value. Using coroutines just to step
across two sequences simultaneously seems unnecessarily complex, and not
terribly efficient compared to a simple pointer increment. But it does
offer a tremendous amount of power for more complex cases.
> As for bi-directional iterators... I actually can't think of anywhere
> I'd use them. I just keep thinking "I could just use random access to
> the same end". Python's never suffered from not having them...
I don't think I've ever needed them either, but they can come in handy
when implementing one container in terms of another. Say an iterable
stack and queue (unlikely, I know) that are implemented via a doubly
linked-list.
Sean
More information about the Digitalmars-d
mailing list