That's all fine...until 2 separate threads happen to iterate on a single collection object.  Separating the cursor position from the collection instance allows for a more flexible sharing model.

Rick


On 5/22/06, Rony G. Flatscher <Rony.Flatscher@wu-wien.ac.at> wrote:
Rick McGuire wrote:
I'm in favor of PREVIOUS and NEXT with a required operand, and doing the wrapping, I'm not in favor of the optional flavor nor of the cursor operation.  Introducing a cursored-type iteration to this class is inconsistent with the other collection classes.  A cursored type iteration should be done as a secondary helper class, not by inserting a cursor position inside the collection itself.

O.K. as I am after the optional flavor (therefore also the need for some sort of a cursor) and OTOH I not wanting to break any logic/assumptions of the "basic" ooRexx collection classes, so I might as well implement that behaviour in a private class of mine (see below).

Having the argument to be optional allows for creating code easier, because there is no need on the programmer's side to maintain index positions (cursors) on their own. E.g. the following programs does not maintain any index positions on its own:
o=.NextableCircularQueue~of("field1", "field2", "field3")

say "cursor at index:" o~cursor
call work1next o -- invoke a full cycle of nexts
say "cursor at index:" o~cursor
say "---"
call work1previous o -- invoke a full cycle of previous
say "cursor at index:" o~cursor
say "---"
call work2random o -- invoke random next/previous messages
say "cursor at index:" o~cursor
say "---"
call work1next o -- invoke a full cycle of nexts
call work1previous o -- invoke a full cycle of previous
say copies("-",20)
exit

/* the following procedures do not need to know which one is next/previous,
ie. they need not maintain a "current index" on their own */
work1next : procedure
use arg o
do i=1 to o~items
say "work1next: NEXT=["o~next"]" "cursor now at index:" o~cursor
end
return

work1previous : procedure
use arg o
do i=1 to o~items
say "work1previous: PREVIOUS=["o~previous"]" "cursor now at index:" o~cursor
end
return

work2random : procedure
use arg o
ran=random(1, o~items) -- randomly determine how many next/previous
bNext=(random(0,1, time("s"))) -- determine whether to use NEXT or PREVIOUS

say "randomly looping" ran "time(s):"
do i=1 to ran -- cycle randomly over
if bNext then
say " work2: ... NEXT=["o~next"] cursor now at index:" o~cursor
else
say " work2: ... PREVIOUS=["o~previous"] cursor now at index:" o~cursor
end
return

May yield:
cursor at index: 0
work1next: NEXT=[field1] cursor now at index: 1
work1next: NEXT=[field2] cursor now at index: 2
work1next: NEXT=[field3] cursor now at index: 3
cursor at index: 3
---
work1previous: PREVIOUS=[field2] cursor now at index: 2
work1previous: PREVIOUS=[field1] cursor now at index: 1
work1previous: PREVIOUS=[field3] cursor now at index: 3
cursor at index: 3
---
randomly looping 1 time(s):
work2: ... PREVIOUS=[field2] cursor now at index: 2
cursor at index: 2
---
work1next: NEXT=[field3] cursor now at index: 3
work1next: NEXT=[field1] cursor now at index: 1
work1next: NEXT=[field2] cursor now at index: 2
work1previous: PREVIOUS=[field1] cursor now at index: 1
work1previous: PREVIOUS=[field3] cursor now at index: 3
work1previous: PREVIOUS=[field2] cursor now at index: 2
--------------------

Having access to the cursor can even allow to set the position (e.g. if a user put the focus to a different input field, where the input fields are represented as a circular queue, which determines the tabbing order). A matching ooRexx program which could like:
/* A specialisation of CircularQueue which allows to cycle through the
collected items without the need to supply an index value.
*/
::class NextableCircularQueue subclass CircularQueue
::method init -- constructor, needed to preset value of CURSOR
expose cursor
cursor=0
forward class (super) -- let superclass instantiate

::method next -- return next item, wrap around, if necessary
expose cursor

if self~items=0 then -- indicate no items in circular queue
return .nil

if (arg() > 1) then -- too many arguments given?
raise syntax 93.902 array (1) -- raise an error

if arg()>0 then
do
parse arg index -- retrieve index value (must be a whole number)
if datatype(index, "Whole")=.false then -- must be a whole number
raise syntax 93.905 array (1, arg(1))
cursor=index -- use index value as new cursor value
end

cursor=cursor+1 -- increase cursor

if cursor<1 | cursor>self~items then -- start out with first item in collection
cursor=1 -- position on first element in collection

return self~at(cursor) -- return next item object


::method previous -- return previous item, wrap around, if necessary
expose cursor

if self~items=0 then -- indicate no items in circular queue
return .nil

if (arg() > 1) then -- too many arguments given?
raise syntax 93.902 array (1) -- raise an error

if arg()>0 then
do
parse arg index -- retrieve index value (must be a whole number)
if datatype(index, "Whole")=.false then -- must be a whole number
raise syntax 93.905 array (1, arg(1))
cursor=index -- use index value as new cursor value
end

cursor=cursor-1 -- decrease cursor

if cursor<1 | cursor>self~items then -- start out with first item in collection
cursor=self~items -- position on last item in collection
return self~at(cursor) -- return next item object


::method cursor -- return cursor's value
expose cursor
return cursor

::method "cursor=" -- allow setting cursor to a whole number
expose cursor

if (arg() > 1) then -- too many arguments given?
raise syntax 93.902 array (1) -- raise an error

parse arg index -- retrieve index value (must be a whole number)
if datatype(index, "Whole")=.false then -- must be a whole number
raise syntax 93.905 array (1, arg(1))
cursor=index -- use index value as new cursor value
Regards,

---rony