reshape #collect: a little - from: [ENH] on:collect:

Mats Nygren nygren at sics.se
Fri Jun 9 11:50:01 UTC 2000


Francisco, and others,

The following are my thoughts in connection to your proposal:

This is the "grandfather of all collect:"

Collection>>collect: aBlock 
	"Evaluate aBlock with each of the receiver's elements as the argument. 
	Collect the resulting values into a collection like the receiver.
	Answer the new collection."
	| newCollection |
	newCollection _ self species new.
	self do: [:each | newCollection add: (aBlock value: each)].
	^ newCollection

And the SequenceableCollection version looks a lot like yours. The one
thing that I dont like with Collection>>collect: is that the class of the result is
decided upon from the receiver. There is no necessary connection. So I
would prefer this:

Collection>>collect: aBlock as: aCollectionClass
	"Evaluate aBlock with each of the receiver's elements as the argument. 
	Collect the resulting values into a collection, a new instance of aCollectionClass.
	Answer the new collection."
	| newCollection |
	newCollection _ aCollectionClass new.
	self do: [:each | newCollection add: (aBlock value: each)].
	^ newCollection

Collection>>collect: aBlock
	^ self collect: aBlock as: self species

In this scheme one would never (as a rule) override #collect: but
instead #collect:as:. As long as self species gives the desired default type of result

Or maybe the following is better, making it possible to create initial collections with allocated space.

Collection>>collect: aBlock into: aCollection
	"Evaluate aBlock with each of the receiver's elements as the argument. 
	Collect the resulting values into aCollection.
Answer  
	aCollection."

	self do: [:each | aCollection add: (aBlock value: each)].
	^ aCollection

Collection>>collect: aBlock as: aCollectionClass
	^ self collect: aBlock into: aCollectionClass new

Collection>>collect: aBlock
	^ self collect: aBlock as: self species

I believe the last version is the best. Having destilled the matter of collecting one element at a time into an already existing collection.
In this scheme one should preferable override #collect:into: when possible, if not then #collect:as: and only last override #collect:.

The problem with your version is that it doesnt do
a polymorphic dispatch on aCollection, you are assuming that aCollection
is sequenceable, however just because one wants the result to be
sequenceable doesnt mean the argument has to be.

Your examples will look like this with the defintions above and another idea from "(1 to: self) .."

	String on: 'hello' collect: [:each | each asUppercase]
becomes
	'hello' collect: #asUppercase
(another implementaion of) this is already in String so it can be written
	'hello' asUppercase
but thats besides the point.

and

	String on: #( 160 130 161 162 163) collect: [:each | each asCharacter] 

becomes

	#( 160 130 161 162 163) collect: #asCharacter as: String

And other things can be done similary for example:

  (aString collect: #asUppercase as: Set) size

will answer the number of different characters in aString, ignoring case.

The last example shows that some further dispatching is needed for this,
the loop machinery depends on both the source collection and the destination
collection. This can always be achieved by an intermediary, but with the
proposed scheme intermediaries can be dispensed with at the cost of more loops.

A related and probably very useful paradigm is appending to streams,
this deserves to be developed. IM(H;-)O. Unless it already is, havnt looked
at it yet.

No flames please;-)

/Mats





More information about the Squeak-dev mailing list