[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Allow additional parameters to Enumerable#inject

Levin Alexander

2/3/2006 2:58:00 PM

Hi,I wanted to count consecutive elements in an array: [1,1,1,2,2,2,2,3,4,4,4,4].count_streams #=> [3,4,1,4] module Enumerable def count_streams last_seen = nil self.inject([]) { |a, elem| if last_seen == elem then a[-1] += 1 else a << 1 end last_seen = elem a } end endHowever, the assignment to "last" outside of the block seems ugly, soI changed inject to take additional parameters: module Enumerable # works just like regular inject, but passes the additional parameters # to the block def inject_with_state(memo, *other) self.each { |obj| memo, *other = yield(memo, obj, *other) } memo end def count_streams self.inject_with_state([], nil) { |a, elem, last_seen| if last_seen == elem then a[-1] += 1 else a << 1 end [a, elem] } end endIs this a good solution to the problem?I think that inject_with_state could be made fully backwardscompatible to inject, it would be nice if inject could be changed tosupport this. What do you think?Viele Grüße,Levin
4 Answers

Marcin Mielzynski

2/3/2006 3:16:00 PM

0

An array can be used as a workaround:

[1,2,3].inject([[],arg]) { |(a,b), elem|
...
[a,arg]
}

lopex

Robert Klemme

2/3/2006 3:19:00 PM

0

Levin Alexander wrote:
> Hi,
>
> I wanted to count consecutive elements in an array:
>
> [1,1,1,2,2,2,2,3,4,4,4,4].count_streams #=> [3,4,1,4]
>
> module Enumerable
> def count_streams
> last_seen = nil
> self.inject([]) { |a, elem|
> if last_seen == elem then a[-1] += 1 else a << 1 end
> last_seen = elem
> a
> }
> end
> end
>
> However, the assignment to "last" outside of the block seems ugly, so
> I changed inject to take additional parameters:
>
> module Enumerable
> # works just like regular inject, but passes the additional
> parameters # to the block
> def inject_with_state(memo, *other)
> self.each { |obj|
> memo, *other = yield(memo, obj, *other)
> }
> memo
> end
>
> def count_streams
> self.inject_with_state([], nil) { |a, elem, last_seen|
> if last_seen == elem then a[-1] += 1 else a << 1 end
> [a, elem]
> }
> end
> end
>
> Is this a good solution to the problem?
>
> I think that inject_with_state could be made fully backwards
> compatible to inject, it would be nice if inject could be changed to
> support this. What do you think?

You don't need to redefine Enumerable#inject for this.

>> def count_streams(enum)
>> enum.inject([[],nil]) {|(a, last),e| e == last ? a[-1]+=1 : a<<1; [a,
e]}[0]
>> end
=> nil
>> count_streams [1,1,1,2,2,2,2,3,4,4,4,4]
=> [3, 4, 1, 4]

You can as well do somehting like this:

>> def count_streams_2(enum)
>> enum.inject([]) do |a, e|
?> a.empty? || e != a[-1][0] ? a << [e,1] : a[-1][1]+=1
>> a
>> end
>> end
=> nil
>> count_streams_2 [1,1,1,2,2,2,2,3,4,4,4,4]
=> [[1, 3], [2, 4], [3, 1], [4, 4]]
>> count_streams_2([1,1,1,2,2,2,2,3,4,4,4,4]).map {|a| a[1]}
=> [3, 4, 1, 4]

Also, I'm not sure it's a good idea to put this method in Enumerable. Is
it really general enough?

Kind regards

robert

Levin Alexander

2/3/2006 4:09:00 PM

0

On 2/3/06, Robert Klemme <bob.news@gmx.net> wrote:> > I think that inject_with_state could be made fully backwards> > compatible to inject, it would be nice if inject could be changed to> > support this. What do you think?>> You don't need to redefine Enumerable#inject for this.>> >> def count_streams(enum)> >> enum.inject([[],nil]) {|(a, last),e|Thanks, I was not aware that parameters can be grouped like that.But I think that an Enumerable#inject with additional parameters wouldalso be useful for many other things, like removing duplicates: def remove_duplicates1(enum) enum.inject([[], nil]) {|(arr, last),elem| arr << elem unless elem== last; [arr, elem] }[0] end def remove_duplicates2(enum) enum.inject([], nil) {|(arr, elem, last| arr << elem unless elem== last; [arr, elem] } endThe second one seems looks a lot cleaner.> Also, I'm not sure it's a good idea to put this method in Enumerable. Is> it really general enough?No, it probably is not. This was just a for small experiment (*), iwould not put this into a library.(However, it seems to be preferred to put methods into the class"where they belong" in Ruby instead of using helper classes)Thank you,Levin(*) <http://www.rexswain.com/benford.html>&... showed, he said, that the overwhelming odds are that at somepoint in a series of 200 tosses, either heads or tails will come upsix or more times in a row." (0..10_000).select { (0..200).map { rand 2 }.inject([[],nil]) {|(a,last),e| if e==last then a[-1]+=1 else a << 1 end [a,e] }[0].partition { |e| e >= 6 }.first.empty? }.length / 10_000.0 #=> 0.0349"overwhelming odds" --> 97%

matthew.moss.coder

2/3/2006 5:07:00 PM

0

Not really about inject specifically... just attempting another way

a = [1,1,1,2,2,2,2,3,4,4,4,4]

a.zip([0] + a).map { |i, j| i - j }.inject([]) do |s, k|
k.zero? ? s[0...-1] + [s[-1] + 1] : s << k
end