Newsgroups: comp.lang.eiffel
From: tarvydas@turing.toronto.edu (Paul Tarvydas)
Subject: Re: LISTS and CURSORS - a design question
Date: 10 Apr 92 20:16:52 GMT


Here's another possible way to think about your CURSORS problem.

When I first encountered this type of solution, I discovered that my
definition of reuse was too narrow.  Successful reuse means being able
to steal concepts, not just code.

Smalltalk is a wonderful, mature catalogue of OOP concepts and it
contains another way to deal with cursors.  The Smalltalk concepts are
ripe for the picking (and for turning into type-safe code :-).

Smalltalk conquers the CURSOR problem by defining little cursor
barnacles called "streams".  Streams are stand-alone, encapsulated
cursors which know how to walk through a given data structure while
returning each successive item.  Each container data structure is
capable of (creating and) returning a stream on itself.  Stream
objects are independent of the container objects which they can
traverse, hence, you can have as many concurrent streams on a
particular object as you wish.

You create a Stream whenever you want to visit every element of a
container-style data structure.  You visit every item once (ie. until
the stream is empty), then you discard the stream.  If you need to
iterate over the data structure again, you just create a new stream.

BTW. Unix "file descriptors" are streams - you can, concurrently, have
more than one pointer into a file, each pointing to different places
in the file.  You create (and destroy) each file pointer as required
using open (and close).  You advance the pointer using seek (or,
implicitly, read/write).  Each file descriptor contains just enough
state information to remember the "open" attributes and positions
within the file on a per-instance basis.

Below, I've included a working, albeit simplistic, implementation of a
stream in Eiffel (2.3).  STREAM is deferred and defines the expected
behaviour, whereas ARRAYSTREAM is an implementation of a STREAM for
Eiffel's Array class.

To use it, ideally ARRAY would be made to know how to return a stream
on itself, eg.

        arry : ARRAY [INTEGER];
        strm : STREAM [INTEGER];
        ...
        for
          strm := arry.stream;
        until
          strm.empty
        do
          <use strm.item>
          ...
          strm.forth
        end;
        
and,

        class ARRAY [T]
        ...
        feature
          stream : ARRAYSTREAM [T] is
            do
              Result.Create (Current);
            end;
        ...
                
[Realistically, you might not want to modify the base ARRAY class, in
which case the stream creation:

        strm := arry.stream;

would become

        strm.Create (arry);

in the above code.]

Paul Tarvydas
TS Controls
5 Bowness Crt.
Etobicoke, Ont.
Canada
M9B 5Z8
tarvydas@turing.toronto.edu




deferred class
    STREAM [T]
export
    item, replace, empty, forth
feature
    
    item : T is
        -- return current item in the stream
        deferred
        end;
    
    replace (i : T) is
        -- replace current item in the stream
        deferred
        end;
    
    empty : BOOLEAN is
        -- true if stream is empty
        deferred
        end;
    
    forth is
        -- move to next element of the subject data structure
        deferred
        end;
end;


class
    ARRAYSTREAM [T]
export
    repeat STREAM, reset
inherit
    STREAM [T]
feature
    
    index : INTEGER;
    subject : ARRAY [T];
    
    Create (s : like subject) is
        do
            subject := s;
            index := subject.lower;
        end;
    
    empty : BOOLEAN is
        do
            Result := index > subject.upper
        end;
    
    item : T is
        require
            not empty
        do
            Result := subject.item (index);
        end;
    
    forth is
        require
            not empty
        do
            index := index + 1;
        end;
    
    replace (i : T) is
        require
            not empty
        do
            subject.put (i, index);
        end;

end;