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
|