Overriding iteration

spir denis.spir at gmail.com
Fri Mar 4 13:30:39 PST 2011


On 03/04/2011 07:06 PM, Jonathan M Davis wrote:
> On Friday, March 04, 2011 09:13:34 spir wrote:
>> On 03/04/2011 05:43 PM, Steven Schveighoffer wrote:
>>> On Fri, 04 Mar 2011 11:29:08 -0500, Magnus Lie Hetland<magnus at hetland.org>
> wrote:
>>>>  From what I understand, when you override iteration, you can either
>>>> implement the basic range primitives, permitting foreach to
>>>> destructively iterate over your object, or you can implement a custom
>>>> method that's called, and that must perform the iteration. The
>>>> destructiveness of the first option can, of course, be mitigated if you
>>>> use a struct rather than a class, and make sure that anything that
>>>> would be destroyed by popFront() is copied.
>>>>
>>>> What I'm wondering is whether there is a way to do what Python does --
>>>> to construct/return an iterator (or, in this case, a range) that is
>>>> used during the iteration, rather than the object itself?
>>>
>>> That's exactly how to do it.
>>>
>>>> I'm thinking about when you iterate directly over the object here. As
>>>> far as I can see, the solution used in the std.container is to use
>>>> opSlice() for this functionality. In other words, in order to iterate
>>>> over container foo, you need to use foreach(e; foo[])? Is there no way
>>>> to get this functionality directly (i.e., for foreach(e; foo))?
>>>
>>> I believe someone has filed a bug for this, because TDPL has said this
>>> should be possible.
>>>
>>> But with the current compiler, you can use opApply to achieve that
>>> behavior.
>>
>> opApply should work but it is supposed to be slower.
>> Defining range primitives directly on the object/container cannot work as
>> of now, unfortunately, because of a pair of bugs (conflicts in formatValue
>> template definitions between struct/class on one hand and ranges on the
>> other).
>
> You don't _want_ range primitives directly on the container. That would mean
> that everything in your container goes away when you process it. Every
> popFront() call would be removing an element from your container. So, for
> insteance, you try and call find() on your container and everything before what
> you were looking isn't in the container anymore - and if it isn't there at all,
> you have an empty container. You _want_ to have a separate type which is a slice
> of our container and has the range primitives.

Certainly, as long as, on an array-like container, you implement popFront as
     this = this[1..$];
or
     this.elements = this.elements[1..$];
then, yes, iterating on it shrinks it. (Note this works only on array-like 
containers; how would you shrink a tree?) But I prefere using a private index 
an have popFront do:
     ++ this.index;

This is a more general iteration mechanism solution, based on current state of 
the object beeing iterated on. For many kinds of sequences, you needs state 
anyway. How else iterate over the sequence of multiples of 3, or squares of 
natural numbers?

> Now, it could very well be that
>
> foreach(v; container)
>
> should be calling opSlice on the container, allowing you to feed the container
> to foreach directly instead of having to slice it yourself
>
> foreach(v; container[])
>
> but that's just syntactic sugar. You don't want to actually treat your container
> like a range. Ranges should be slices of containers, not containers themselves.

I agree slices should be an alternate iteration mechanism (as said in TDPL). 
But one cannot slice a tree that easily :-) (where's my chain saw?)

Denis
-- 
_________________
vita es estrany
spir.wikidot.com



More information about the Digitalmars-d-learn mailing list