From: Josh Cheek Date: 2012-09-14T20:06:36+09:00 Subject: Re: inject is pathetic? --047d7b2ee4cbc283b204c9a76aeb Content-Type: text/plain; charset=ISO-8859-1 On Fri, Sep 14, 2012 at 5:19 AM, botp wrote: > On Fri, Sep 14, 2012 at 5:46 PM, Josh Cheek wrote: > > strings.inject("") { |joined, current| joined + current } > > > > is slow and wasteful because it created all the intermediate forms: "", > "a", > > "ab", "abc", "abcd", "abcde" > > but josh, isn't that because of String#+ ? > > Yes > there is always String#<< or String#concat. > > You're right, since String#<< returns self, you can do it efficiently with inject: [*'a'..'e'].inject('', :<<) # => "abcde" Instead of [*'a'..'e'].inject('', :+) # => "abcde" It deviates from the more functional ideas of immutable data, which is where inject makes the most sense, but in Ruby this case will work sufficiently. When return values don't match up nicely like this, it gets cumbersome (you have to go to multiple lines or use semicolons): %w[a b a c].inject(Hash.new 0) { |sums, char| sums[char] += 1; sums } %w[a b a c].each_with_object(Hash.new 0) { |char, sums| sums[char] += 1 } > btw, i usually use each.with_object instead of each_with_object. And i > alias with_object to using_object. but hey, that is just me. but > regardless, we all are fan of ruby, right? to each, his each, i guess > ;-) > > I only do .with_object when I'm working with an arbitrary enumerator, because I expect it has overhead associated with its laziness. I don't know how it is implemented (it takes a lot of effort for me to figure out what C Ruby is doing), but I think of it basically like this: List = Struct.new :element, :child do def each_with_object(object, &block) block[element, object] child && child.each_with_object(object, &block) object end def each(&block) return LazyIteration.new self, :each unless block block[element] child && child.each(&block) end LazyIteration = Struct.new :target, :message do def with_object(object, &block) target.send(message) { |element| block[element, object] } object end end end list = List.new('a', List.new('b', List.new('c', nil))) list.each_with_object('') { |element, aggregate| aggregate << element } # => "abc" list.each.with_object('') { |element, aggregate| aggregate << element } # => "abc" --047d7b2ee4cbc283b204c9a76aeb Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable On Fri, Sep 14, 2012 at 5:19 AM, botp <botpena@gmail.com> wr= ote:
On Fri, Sep 14, 2012 at 5:46 PM, Josh Cheek <josh.cheek@gmail.com> wrote:
> strings.inject("") { |joined, current| joined + current } >
> is slow and wasteful because it created all the intermediate forms: &q= uot;", "a",
> "ab", "abc", "abcd", "abcde"
but josh, isn't that because of String#+ =A0?


Yes
=A0
there is always String#<< or String#concat.


You're right, since String#<< returns s= elf, you can do it efficiently with inject:

[*'a'..'e= 9;].inject('', :<<) # =3D> "abcde"

Instea= d of

[*'a'..'e'].inject('', :+) # =3D> "abcd= e"

It deviates from the more functional ideas of immutable data= , which is where inject makes the most sense, but in Ruby this case will wo= rk sufficiently.


When return values don't match up nicely like this, it=A0 gets = cumbersome (you have to go to multiple lines or use semicolons):

%w[= a b a c].inject(Hash.new 0) { |sums, char| sums[char] +=3D 1; sums }
%w[= a b a c].each_with_object(Hash.new 0) { |char, sums| sums[char] +=3D 1 }


btw, i usually use each.with_object instead of each_with_object. And i
alias with_object to using_object. =A0but hey, that is just me. but
regardless, we all are fan of ruby, right? =A0to each, his each, i guess =A0;-)


I only do .with_object when I'm working with = an arbitrary enumerator, because I expect it has overhead associated with i= ts laziness. I don't know how it is implemented (it takes a lot of effo= rt for me to figure out what C Ruby is doing), but I think of it basically = like this:


List =3D Struct.new :element, :child do
=A0 def each_with_object= (object, &block)
=A0=A0=A0 block[element, object]
=A0=A0=A0 child= && child.each_with_object(object, &block)
=A0=A0=A0 object<= br>=A0 end
=A0
=A0 def each(&block)
=A0=A0=A0 return LazyIteration.new self, :each = unless block
=A0=A0=A0 block[element]
=A0=A0=A0 child && chil= d.each(&block)
=A0 end
=A0
=A0 LazyIteration =3D Struct.new := target, :message do
=A0=A0=A0 def with_object(object, &block)
=A0=A0=A0=A0=A0 target.send(message) { |element| block[element, object] }=A0=A0=A0=A0=A0 object
=A0=A0=A0 end
=A0 end
end

list =3D= List.new('a', List.new('b', List.new('c', nil)))
list.each_with_object('') { |element, aggregate| aggregate &l= t;< element } # =3D> "abc"
list.each.with_object('') { |element, aggregate| aggregate <<= element } # =3D> "abc"

--047d7b2ee4cbc283b204c9a76aeb--