I would like to know how to stream a collection backwards without copies in Pharo/Squeak.
For example, to stream #(1 2 3)
so stream next
returns 3
, then 2
, then 1
. I know I could just use collection reversed readStream
, but reversed
copies.
You could use a Generator:
| coll stream |
coll := #(1 2 3).
stream := Generator on: [:g | coll reverseDo: [:ea | g yield: ea]].
stream next
Generators let you wrap a streaming interface around any piece of code, basically.
Create the RevertingCollection
class as a subclass of SequeanceableCollection
with one instance variable collection
. Now define these three methods (instance side):
on: aCollection
collection := aCollection
size
^collection size
at: index
^collection at: self size - index + 1
Done. You can now do the following:
stream := (RevertingCollection new on: #(1 2 3)) readStream.
and you will get
stream next "3".
stream next "2".
stream next "1"
You can go a step further and implement the message
SequenceableCollection >> #reverseStream
^(RevertingCollection new on: self) readStream
In this way everything reduces to just
#(1 2 3) reverseStream
ADDENDUM
As discussed in the comments there are two pieces missing here which are:
1. An instance creation method (class side)
RevertingCollection class >> #on: aCollection
^self new on: aCollection
With this addition the method above should be rewritten to:
SequenceableCollection >> #reverseStream
^(RevertingCollection on: self) readStream
Note: Other smalltalkers would prefer this method to be named #withAll:
.
2. The following method for copying:
RevertingCollection >> #copyFrom: start to: stop
| n |
n := self size.
copy := collection copyFrom: n - stop + 1 to: n - start + 1.
^self class on: copy
This method is required to support #next:
in the reverse read stream.
There are three options off the top of my head:
- Modify your code to use
#reverseDo:
- Use Xtreams
- Roll your own stream