On Fri, Sep 14, 2012 at 5:19 AM, botp <botpena / gmail.com> wrote:

> 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: "",
> "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"