Set + collect:

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Set + collect:

Germán Leiva
Hi, this is my first post (please be gentle xD)

The #collect: message commonly return a new collection like the receiver, but this is really unpractical in some cases

e.g.
col := Set with: 'one'->1 with: 'two'->2 with: 'dos'->2.
(col collect: [:each | each value ]) sum

In this example #sum will return 3 instead of 5 (I searched a test for #sum but I didn´t find it).
 
Ok, I can use the message #asBag (or something like that) but does the second line have to depend on the type of the receiver (i.e. the type of col)?

For example, the implementation of #collect: in SortedCollection returns an OrderedCollection (in the current image) and in other dialects the message #collect: when the receiver is a Set returns a Bag ...

Basically, what do you think of an implementation like this?
 
Set >> collect: aBlock
 "Evaluate aBlock with each of the receiver's elements as the argument. 
 Collect the resulting values into a Bag. Answer the new collection."
 | newBag |
 newBag := Bag new: self size.
 array do: [:each | each ifNotNil: [newBag add: (aBlock value: each)]].
 ^ newBag

Cordially,
--
Germán Leiva
[hidden email]

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: Set + collect:

Stéphane Ducasse

On May 3, 2010, at 9:31 PM, Germán Leiva wrote:

> Hi, this is my first post (please be gentle xD)

Thanks!

>
> The #collect: message commonly return a new collection like the receiver, but this is really unpractical in some cases
>
> e.g.
> col := Set with: 'one'->1 with: 'two'->2 with: 'dos'->2.
> (col collect: [:each | each value ]) sum

In VW you get the same.

> In this example #sum will return 3 instead of 5 (I searched a test for #sum but I didn´t find it).
>  
> Ok, I can use the message #asBag (or something like that) but does the second line have to depend on the type of the receiver (i.e. the type of col)?

I think that this is the invariant for collect: ?

> For example, the implementation of #collect: in SortedCollection returns an OrderedCollection (in the current image)

in VW too

> and in other dialects the message #collect: when the receiver is a Set returns a Bag ...

which ones?

> Basically, what do you think of an implementation like this?
>  
> Set >> collect: aBlock
>  "Evaluate aBlock with each of the receiver's elements as the argument.  
>  Collect the resulting values into a Bag. Answer the new collection."
>  | newBag |
>  newBag := Bag new: self size.
>  array do: [:each | each ifNotNil: [newBag add: (aBlock value: each)]].
>  ^ newBag
>
> Cordially,
> --
> Germán Leiva
> [hidden email]
> _______________________________________________
> Pharo-project mailing list
> [hidden email]
> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project


_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: Set + collect:

Nicolas Cellier
In reply to this post by Germán Leiva
True.It can be impractical, but no need to change #collect:
Please use #detectSum:

(Set with: 'one'->1 with: 'two'->2 with: 'dos'->2) detectSum: [:e |e value].

or #collect:as:

((Set with: 'one'->1 with: 'two'->2 with: 'dos'->2) collect: [:e |e
value] as: Array) sum.

Nicolas

2010/5/3 Germán Leiva <[hidden email]>:

> Hi, this is my first post (please be gentle xD)
>
> The #collect: message commonly return a new collection like the receiver,
> but this is really unpractical in some cases
> e.g.
> col := Set with: 'one'->1 with: 'two'->2 with: 'dos'->2.
> (col collect: [:each | each value ]) sum
> In this example #sum will return 3 instead of 5 (I searched a test for #sum
> but I didn´t find it).
>
> Ok, I can use the message #asBag (or something like that) but does the
> second line have to depend on the type of the receiver (i.e. the type of
> col)?
> For example, the implementation of #collect: in SortedCollection returns an
> OrderedCollection (in the current image) and in other dialects the message
> #collect: when the receiver is a Set returns a Bag ...
>
> Basically, what do you think of an implementation like this?
>
> Set >> collect: aBlock
>  "Evaluate aBlock with each of the receiver's elements as the argument.
>  Collect the resulting values into a Bag. Answer the new collection."
>  | newBag |
>  newBag := Bag new: self size.
>  array do: [:each | each ifNotNil: [newBag add: (aBlock value: each)]].
>  ^ newBag
> Cordially,
> --
> Germán Leiva
> [hidden email]
>
> _______________________________________________
> Pharo-project mailing list
> [hidden email]
> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
>

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: Set + collect:

Germán Leiva
2010/5/3 Stéphane Ducasse <[hidden email]> 
> Hi, this is my first post (please be gentle xD)

Thanks!

xD
 
>
> The #collect: message commonly return a new collection like the receiver, but this is really unpractical in some cases
>
> e.g.
> col := Set with: 'one'->1 with: 'two'->2 with: 'dos'->2.
> (col collect: [:each | each value ]) sum

In VW you get the same.

Yes

> In this example #sum will return 3 instead of 5 (I searched a test for #sum but I didn´t find it).
>
> Ok, I can use the message #asBag (or something like that) but does the second line have to depend on the type of the receiver (i.e. the type of col)?

I think that this is the invariant for collect: ?

I didn´t understand that. 
I'll like that this line (col collect: [:each | each value ]) sum 
always returns 5 independently of the class of col (if col has the elements 'one'->1 , 'two'->2 and 'dos'->2).

> For example, the implementation of #collect: in SortedCollection returns an OrderedCollection (in the current image)

in VW too

Yes, I like that. I just put the example to show that #collect: not always returns a new collection like the receiver. 

> and in other dialects the message #collect: when the receiver is a Set returns a Bag ...

which ones?


In IBM VisualAge 6.0 returns a Bag (in Instantiations VA 7.5.2 returns a Set).

> Basically, what do you think of an implementation like this?
>
> Set >> collect: aBlock
>  "Evaluate aBlock with each of the receiver's elements as the argument.
>  Collect the resulting values into a Bag. Answer the new collection."
>  | newBag |
>  newBag := Bag new: self size.
>  array do: [:each | each ifNotNil: [newBag add: (aBlock value: each)]].
>  ^ newBag

2010/5/4 Nicolas Cellier <[hidden email]>
True.It can be impractical, but no need to change #collect:
Please use #detectSum:

(Set with: 'one'->1 with: 'two'->2 with: 'dos'->2) detectSum: [:e |e value].
 
or #collect:as:

((Set with: 'one'->1 with: 'two'->2 with: 'dos'->2) collect: [:e |e
value] as: Array) sum.

Nicolas

Nice =) I didn't know those messages (although IMHO the name #detectSum: is not pretty - I'm thinking aloud here ... #acummulateSum: or: directly #sum: ...?)

The reason I'm proposing to change the Set >> #collect: implementation is because the only situtation when you need that #collect: returns a Set is when you didn't want duplications and for that is really nice to explicity send the message asSet.

In all other cases a Bag is all you need as response to collect (when collect is sended to a Set).

If you say this is counterproductive and the change will generate more headaches than happiness I understand xD

Cheers,



2010/5/3 Germán Leiva <[hidden email]>:
> Hi, this is my first post (please be gentle xD)
>
> The #collect: message commonly return a new collection like the receiver,
> but this is really unpractical in some cases
> e.g.
> col := Set with: 'one'->1 with: 'two'->2 with: 'dos'->2.
> (col collect: [:each | each value ]) sum
> In this example #sum will return 3 instead of 5 (I searched a test for #sum
> but I didn´t find it).
>
> Ok, I can use the message #asBag (or something like that) but does the
> second line have to depend on the type of the receiver (i.e. the type of
> col)?
> For example, the implementation of #collect: in SortedCollection returns an
> OrderedCollection (in the current image) and in other dialects the message
> #collect: when the receiver is a Set returns a Bag ...
>
> Basically, what do you think of an implementation like this?
>
> Set >> collect: aBlock
>  "Evaluate aBlock with each of the receiver's elements as the argument.
>  Collect the resulting values into a Bag. Answer the new collection."
>  | newBag |
>  newBag := Bag new: self size.
>  array do: [:each | each ifNotNil: [newBag add: (aBlock value: each)]].
>  ^ newBag
> Cordially,
> --
> Germán Leiva
> [hidden email]
>
> _______________________________________________
> Pharo-project mailing list
> [hidden email]
> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
>

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project



--
Germán Leiva
[hidden email]

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
Reply | Threaded
Open this post in threaded view
|

Re: Set + collect:

Nicolas Cellier
2010/5/6 Germán Leiva <[hidden email]>:

> 2010/5/3 Stéphane Ducasse <[hidden email]>
>>
>> > Hi, this is my first post (please be gentle xD)
>>
>> Thanks!
>>
> xD
>
>>
>> >
>> > The #collect: message commonly return a new collection like the
>> > receiver, but this is really unpractical in some cases
>> >
>> > e.g.
>> > col := Set with: 'one'->1 with: 'two'->2 with: 'dos'->2.
>> > (col collect: [:each | each value ]) sum
>>
>> In VW you get the same.
>>
> Yes
>
>> > In this example #sum will return 3 instead of 5 (I searched a test for
>> > #sum but I didn´t find it).
>> >
>> > Ok, I can use the message #asBag (or something like that) but does the
>> > second line have to depend on the type of the receiver (i.e. the type of
>> > col)?
>>
>> I think that this is the invariant for collect: ?
>>
> I didn´t understand that.
> I'll like that this line (col collect: [:each | each value ]) sum
> always returns 5 independently of the class of col (if col has the
> elements 'one'->1 , 'two'->2 and 'dos'->2).
>
>> > For example, the implementation of #collect: in SortedCollection returns
>> > an OrderedCollection (in the current image)
>>
>> in VW too
>>
> Yes, I like that. I just put the example to show that #collect: not always
> returns a new collection like the receiver.
>
>> > and in other dialects the message #collect: when the receiver is a Set
>> > returns a Bag ...
>>
>> which ones?
>>
>
> In IBM VisualAge 6.0 returns a Bag (in Instantiations VA 7.5.2 returns a
> Set).
>
>> > Basically, what do you think of an implementation like this?
>> >
>> > Set >> collect: aBlock
>> >  "Evaluate aBlock with each of the receiver's elements as the argument.
>> >  Collect the resulting values into a Bag. Answer the new collection."
>> >  | newBag |
>> >  newBag := Bag new: self size.
>> >  array do: [:each | each ifNotNil: [newBag add: (aBlock value: each)]].
>> >  ^ newBag
>
> 2010/5/4 Nicolas Cellier <[hidden email]>
>>
>> True.It can be impractical, but no need to change #collect:
>> Please use #detectSum:
>>
>> (Set with: 'one'->1 with: 'two'->2 with: 'dos'->2) detectSum: [:e |e
>> value].
>>
>>
>> or #collect:as:
>>
>> ((Set with: 'one'->1 with: 'two'->2 with: 'dos'->2) collect: [:e |e
>> value] as: Array) sum.
>>
>> Nicolas
>
> Nice =) I didn't know those messages (although IMHO the name #detectSum: is
> not pretty - I'm thinking aloud here ... #acummulateSum: or: directly #sum:
> ...?)

Sure, the name is just bad...
I usually reimplement #sumOf: #maxOf: etc... or just #sum:
#accumulateSum: sounds a bit like #cumulativeSum: (Matlab cumsum)

(1 to: 5) cumulativeSum: [:e | e squared] -> #( 1 5 16 32 57 )

This message is not in Pharo nor Squeak though

> The reason I'm proposing to change the Set >> #collect: implementation is
> because the only situtation when you need that #collect: returns a Set is
> when you didn't want duplications and for that is really nice to explicity
> send the message asSet.
> In all other cases a Bag is all you need as response to collect (when
> collect is sended to a Set).

Ah, if you come with a Rationale, that makes sense.
I first thought you were willing to change core just to solve a specific problem

> If you say this is counterproductive and the change will generate more
> headaches than happiness I understand xD
> Cheers,
>

I just can't say... The problem is we never know which code will break...
This would deserve further inquiries.
All I can say is #collect:as: fits perfectly your needs.

It's always possible to change the core API.
We recently changed Dictionary keys to answer an Array rather than a Set.
It was rude because an Array cannot #add: nor #remove:
The rationale was:
- homegeneity with #values which did already answer an Array rather than a Bag,
  bonus: the #keys are now sorted like the #values
- performance - because Arrays are much faster than Sets
- core image analysis showing very few usage of add:/remove: on keys.
- low upgrade cost (just adding an explicit asSet here and there like
you're proposing).

We also changed Dictionary>>collect: to answer a Dictionary rather
than an OrderedCollection in Squeak 3.10 (or a Bag in very old st80).

I also have the feeling that Bags are a bit segregated these days...

Nicolas

>>
>>
>> 2010/5/3 Germán Leiva <[hidden email]>:
>> > Hi, this is my first post (please be gentle xD)
>> >
>> > The #collect: message commonly return a new collection like the
>> > receiver,
>> > but this is really unpractical in some cases
>> > e.g.
>> > col := Set with: 'one'->1 with: 'two'->2 with: 'dos'->2.
>> > (col collect: [:each | each value ]) sum
>> > In this example #sum will return 3 instead of 5 (I searched a test for
>> > #sum
>> > but I didn´t find it).
>> >
>> > Ok, I can use the message #asBag (or something like that) but does the
>> > second line have to depend on the type of the receiver (i.e. the type of
>> > col)?
>> > For example, the implementation of #collect: in SortedCollection returns
>> > an
>> > OrderedCollection (in the current image) and in other dialects the
>> > message
>> > #collect: when the receiver is a Set returns a Bag ...
>> >
>> > Basically, what do you think of an implementation like this?
>> >
>> > Set >> collect: aBlock
>> >  "Evaluate aBlock with each of the receiver's elements as the argument.
>> >  Collect the resulting values into a Bag. Answer the new collection."
>> >  | newBag |
>> >  newBag := Bag new: self size.
>> >  array do: [:each | each ifNotNil: [newBag add: (aBlock value: each)]].
>> >  ^ newBag
>> > Cordially,
>> > --
>> > Germán Leiva
>> > [hidden email]
>> >
>> > _______________________________________________
>> > Pharo-project mailing list
>> > [hidden email]
>> > http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
>> >
>>
>> _______________________________________________
>> Pharo-project mailing list
>> [hidden email]
>> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
>
>
>
> --
> Germán Leiva
> [hidden email]
>
> _______________________________________________
> Pharo-project mailing list
> [hidden email]
> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project
>

_______________________________________________
Pharo-project mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project