another confusing result

classic Classic list List threaded Threaded
4 messages Options
larrry larrry
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

another confusing result

This may just be me misunderstanding the way it's supposed to work, but I was exploring the method 

combinations: kk atATimeDo: aBlock defined in SequenceableCollection

'abcde' combinations: 2 atATimeDo: [:each | Transcript cr; show: each printString]. 
produces the expected result:
#($a $b)
#($a $c)
#($a $d)
#($a $e)
#($b $c)
#($b $d)
#($b $e)
#($c $d)
#($c $e)
#($d $e)

but, if you add :each to a collection instead of printing, you get something different. The following code:
res := OrderedCollection new. 
 'abcde' combinations: 2 atATimeDo: [:each | res add: each]. 
Transcript show: res.
produces:
an OrderedCollection(#($e $e) #($e $e) #($e $e) #($e $e) #($e $e) #($e $e) #($e $e) #($e $e) #($e $e) #($e $e))

Thanks again.
Benjamin Van Ryseghem-2 Benjamin Van Ryseghem-2
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: another confusing result


On Jan 8, 2012, at 4:16 PM, Larry White wrote:

> This may just be me misunderstanding the way it's supposed to work, but I was exploring the method
>
> combinations: kk atATimeDo: aBlock defined in SequenceableCollection
>
> 'abcde' combinations: 2 atATimeDo: [:each | Transcript cr; show: each printString].
> produces the expected result:
> #($a $b)
> #($a $c)
> #($a $d)
> #($a $e)
> #($b $c)
> #($b $d)
> #($b $e)
> #($c $d)
> #($c $e)
> #($d $e)

And here, if you do:
'abcde' combinations: 2 atATimeDo: [:each | each inspect. Transcript cr; show: each printString].
You get inspectors with #($e $e) ...

>
> but, if you add :each to a collection instead of printing, you get something different. The following code:
> res := OrderedCollection new.
>  'abcde' combinations: 2 atATimeDo: [:each | res add: each].
> Transcript show: res.
> produces:
> an OrderedCollection(#($e $e) #($e $e) #($e $e) #($e $e) #($e $e) #($e $e) #($e $e) #($e $e) #($e $e) #($e $e))
>
> Thanks again.


After a quick look at the methods

combinations: kk atATimeDo: aBlock
        "Take the items in the receiver, kk at a time, and evaluate the block for each combination.  Hand in an array of elements of self as the block argument.  Each combination only occurs once, and order of the elements does not matter.  There are (self size take: kk) combinations."
        " 'abcde' combinations: 3 atATimeDo: [:each | Transcript cr; show: each printString]"

        | aCollection |
        aCollection := Array new: kk.
        self combinationsAt: 1 in: aCollection after: 0 do: aBlock

and

combinationsAt: jj in: aCollection after: nn do: aBlock
        "Choose k of N items and put in aCollection.  jj-1 already chosen.  Indexes of items are in numerical order, to avoid the same combo being used twice.  In this slot, we are allowed to use items in self indexed by nn+1 to self size.  nn is the index used for position jj-1."
        "(1 to: 6) combinationsSize: 3 do: [:each | Transcript cr; show: each printString]"

nn+1 to: self size do: [:index |
                aCollection at: jj put: (self at: index).
                jj = aCollection size
                        ifTrue: [aBlock value: aCollection]
                        ifFalse: [self combinationsAt: jj + 1 in: aCollection after: index do: aBlock]].

You can see that each step result is store in the same collection.
So actually, when you print it it's ok, because it's done before the following step erase the current content of the collection.
But then at the end, you get always the same result, it's what the code is supposed to do.

Then, I do not think it makes sense, but at least, now I understand why ;)


Ben
Runar Jordahl Runar Jordahl
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: another confusing result

Here is a copy of something I wrote about this earlier:

In Pharo 1.3, SequenceableCollection>>combinations:atATimeDo: will,
for all combinations, send the same collection instance as argument
for the block. Therefore, if you use this collection itself, you will
be surprised:

|answer|
answer := OrderedCollection new.
#(a b c) combinations: 2 atATimeDo: [:each | answer add: each].
answer

Here I expect to end up with a collection looking like this:
1:  #(#a #b)
2:  #(#a #c)
3:  #(#b #c)

But I end up with:
1:  #(#c #c)
2:  #(#c #c)
3:  #(#c #c)

One fix is to change the client code to copy the argument for the block:
|answer|
answer := OrderedCollection new.
#(a b c) combinations: 2 atATimeDo: [:each | answer add: each copy].
answer

Another solution is enhancing
SequenceableCollection>>combinationsAt:in:after:do: to copy the
collection:
nn + 1 to: self size do: [ :index |
               aCollection at: jj put: (self at: index).
               jj = aCollection size
                       ifTrue: [ aBlock value: aCollection copy ]
                       ifFalse: [
                               self
                                       combinationsAt: jj + 1
                                       in: aCollection
                                       after: index
                                       do: aBlock ] ]



Kind regards
Runar Jordahl

larrry larrry
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: another confusing result

Thanks.  I wonder if this method needs to stay the same for compatibility reasons or if it could be modified in Pharo to produce a less surprising result. In other words, is this a bug or a feature? 

On Mon, Jan 9, 2012 at 1:41 PM, Runar Jordahl <[hidden email]> wrote:
Here is a copy of something I wrote about this earlier:

In Pharo 1.3, SequenceableCollection>>combinations:atATimeDo: will,
for all combinations, send the same collection instance as argument
for the block. Therefore, if you use this collection itself, you will
be surprised:

|answer|
answer := OrderedCollection new.
#(a b c) combinations: 2 atATimeDo: [:each | answer add: each].
answer

Here I expect to end up with a collection looking like this:
1:  #(#a #b)
2:  #(#a #c)
3:  #(#b #c)

But I end up with:
1:  #(#c #c)
2:  #(#c #c)
3:  #(#c #c)

One fix is to change the client code to copy the argument for the block:
|answer|
answer := OrderedCollection new.
#(a b c) combinations: 2 atATimeDo: [:each | answer add: each copy].
answer

Another solution is enhancing
SequenceableCollection>>combinationsAt:in:after:do: to copy the
collection:
nn + 1 to: self size do: [ :index |
              aCollection at: jj put: (self at: index).
              jj = aCollection size
                      ifTrue: [ aBlock value: aCollection copy ]
                      ifFalse: [
                              self
                                      combinationsAt: jj + 1
                                      in: aCollection
                                      after: index
                                      do: aBlock ] ]



Kind regards
Runar Jordahl


Loading...