5

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.

Amos M. Carpenter
  • 4,848
  • 4
  • 42
  • 72
juan
  • 59
  • 1

3 Answers3

3

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.

Leandro Caniglia
  • 14,495
  • 4
  • 29
  • 51
  • 2
    Objects are beautiful! :) – Sean DeNigris Mar 22 '15 at 11:29
  • Indeed. And that's key because aesthetics stimulates learning and creativity. – Leandro Caniglia Mar 22 '15 at 12:54
  • 1
    Personally, I'd add a class-side `withAll:` method and an accessor for the collection (using `on:` on the instance-side makes me think of streams, not sure why - if you're worried about encapsulation, put the accessor in the "private" protocol), but this is a very nice answer. :-) – Amos M. Carpenter Mar 24 '15 at 07:37
  • @AmosM.Carpenter I totally agree with you. I didn't go in further details in my answer for the sake of conciseness. A more complete implementation would need some instance creation methods (as the one you suggest) plus the instance `#copyFrom:to:` required to support `#next:`. I will probably extend my answer later today. – Leandro Caniglia Mar 24 '15 at 16:47
3

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.

codefrau
  • 4,583
  • 17
  • 17
  • I don't know what a Generator is so I'm opening a new question. – Leandro Caniglia Mar 24 '15 at 22:49
  • Oh, I've just seen that Generator is a block based Stream and is present in Pharo. No need to ask. Very nice answer. Thanks. – Leandro Caniglia Mar 24 '15 at 23:00
  • Hadn't heard of Generators either, but it seems simple and efficient. I'd really recommend getting away from those "un-Smalltalk-like" abbreviations, though. Smalltalk is all about readability. ;-) – Amos M. Carpenter Mar 25 '15 at 00:28
  • @AmosM.Carpenter: If this was production code I'd use full variable names of course. But this is a workspace example, so brevity is a virtue, IMHO. – codefrau Mar 25 '15 at 10:38
  • I respectfully disagree with that. Brevity in code is never a virtue (except maybe when you're desperately trying to save on bandwidth), and why would sacrificing readability and clarity improve something that is meant as a teaching example to others? Code is written once, but read many, many times - always cater for the code reader, not the writer. I've come across way too much production code that was meant to be "cleaned up for production" later... but I'll stop preaching now, I'm sure you know what I mean. ;-) – Amos M. Carpenter Mar 25 '15 at 10:46
  • StackOverflow gives us the opportunity to show Smalltalk to a very broad community. Let's take full advantage of the capabilities and rules of the site to enhance our questions and answers. If something can be improved, just go and edit it. Suggesting a change in a comment is not as efficient and leads to discussions more appropriate for other sites. – Leandro Caniglia Mar 25 '15 at 13:00
  • That is really cool! I didn't even know `Generator` was in the image – Sean DeNigris Mar 30 '15 at 21:29
2

There are three options off the top of my head:

  1. Modify your code to use #reverseDo:
  2. Use Xtreams
  3. Roll your own stream
Sean DeNigris
  • 6,306
  • 1
  • 31
  • 37