Mattia Barbon wrote:
>>     set P1, P0         # tells P1 that he's going to iterate P0
>
> This looks like a kludge. IMHO the correct way of getting an iterator
> is having the aggregate return it (say, using find_method/invoke);

that's a correct assumption, and when find_method/invoke will be
implemented, there will be something like that. but this doesn't
change much in the Iterator implementation. it will be just another
way (probably the preferred one) to get an Iterator.

<rant>
either way, the PASM language is not intended for humans(*), doesn't
strive for inner elegance and perfect orthogonality, so kludges are
legal as long as they gain us speed :-)

(*) read: it is not intended to be something you will have fun(**)
programming in, there's Perl for this.

(**) but then, there are people actually having fun programming in
Befunge, so this statement could not apply at all :-)
</rant>

> and the iterator does not need to be of class Iterator, it just needs
> to behave like one. Otherwise your Iterator needs to know all the
> types it has to iterate over, which is bad.

that's not the case. the Iterator *need* to be of class Iterator, as
I'm going to demonstrate. as I implemented it, it actually doesn't need
to know all the types it has to iterate over, as I'm going to show.
on the other hand, the current Iterator PMC does not actually
*implement* an iterator, but in some sense it behaves like one. it's
the aggregate itself that needs to implement the methods to iterate
its own data structure, so what you say is partly correct.

the design of Iterator.pmc has the following objectives:
1. provide an abstract (read: aggregate-independant) interface to
   iterate an aggregate
2. provide an object that is external to the aggregate, so that
   one aggregate can have multiple iterators

point 1. is accomplished through vtable entries. the aggregate
should implement the following functions for Iterator to work:

  INTVAL iterator_sizeof()
  void   iterator_reset()
  void   iterator_set(void *data)
  void   iterator_get(void *data)
  PMC*   iterator_get_current()
  void   iterator_next();

once an Iterator is "assigned" to an aggregate PMC, it calls its
iterator_sizeof() method.

INTVAL iterator_sizeof()
this should return the amount of memory required to store and mantain
iterator state information. for example, iterating an Array only
requires an INTVAL (the current index in the array), so sizeof(INTVAL)
will be returned. iterating an Hash requires two values (the bucket
chain index and a pointer to the current HashBucket, but these are
details), so PerlHash's iterator_sizeof() will return
sizeof(HashIndex) + sizeof(HASHBUCKET*). the Iterator PMC allocates
the proper amount of memory so that it can mantain its own state
information (see point 2.).

void iterator_set(void *data)
this sets the aggregate's iterator state to the given "data", eg. the
amount of memory allocated with iterator_sizeof().

void iterator_get(void *data)
this gets the aggregate's iterator state so that the Iterator PMC can
update its own "data".

PMC* iterator_get_current()
void iterator_reset()
void iterator_next()
these are the methods that actually does the iteration.

as you can see, all the iteration stuff is done by the aggregate
PMC class itself. the Iterator PMC provides opcode mapping for
its action (eg. inc for next, etc.), as well as storage for its
own state. this way, an aggregate (which really does the iteration)
does not need to mantain such information for multiple iterators.
it only needs to receive the relevant information from the Iterator
who's currently acting on the aggregate.
the iterator updates its own memory from the aggregate on each
operation, as the following pseudocode shows (hope it's clear :-):

  reset {
    AGGREGATE->iterator_reset()
    ITERATOR->data = AGGREGATE->iterator_get()
  }

  next {
    AGGREGATE->iterator_set(ITERATOR->data)
    AGGREGATE->iterator_next()
    ITERATOR->data = AGGREGATE->iterator_get()
  }

  get_current {
    AGGREGATE->iterator_set(ITERATOR->data)
    return AGGREGATE->iterator_get_current
  }

this accomplishes point 2. and shows why Iterator needs to be a class
of its own.

the implementation could surely be improved, and it certainly need
to(*), but I hope the design does make some sense :-)

(*) just to make an example, this approach is absolutely not
thread-safe.


cheers,
Aldo

__END__
$_=q,just perl,,s, , another ,,s,$, hacker,,print;

Reply via email to