Forum: Ruby inject is pathetic?

Posted by Dave Aronson (Guest)
on 2012-09-13 20:31
(Received via mailing list)
On Wed, Sep 12, 2012 at 11:46 PM, 7stud -- <lists@ruby-forum.com> wrote:

> Just forget inject() even exists.  It's really quite a pathetic method.

What do you mean by that?

-Dave
Posted by Matt Neuburg (Guest)
on 2012-09-14 05:00
(Received via mailing list)
Dave Aronson <rubytalk2dave@davearonson.com> wrote:

> On Wed, Sep 12, 2012 at 11:46 PM, 7stud -- <lists@ruby-forum.com> wrote:
>
> > Just forget inject() even exists.  It's really quite a pathetic method.
>
> What do you mean by that?

He probably just doesn't grasp how fundamental it is. After all, no one
would call "join" pathetic, but it is simply a special case of inject.
Or, to put it a better way, inject abstracts intuitively obvious notions
such as join, sum, and product. And in Ruby 1.9.3 it gets even cooler,
since you can just say:

   [1,2,3].inject(:+)

Not everyone knows LISP or has read The Structure and Interpretation of
Computer Programs. Those who have, know. m.
Posted by Zhi-Qiang Lei (Guest)
on 2012-09-14 05:25
(Received via mailing list)
Agree. #inject is essential for Array.

On Sep 14, 2012, at 11:00 AM, Matt Neuburg wrote:

> Or, to put it a better way, inject abstracts intuitively obvious notions
> A fool + a tool + an autorelease pool = cool!
> AppleScript: the Definitive Guide - Second Edition!
> http://www.tidbits.com/matt/default.html#applescriptthings
>
>


Best regards,
Zhi-Qiang Lei
zhiqiang.lei@gmail.com
Posted by Josh Cheek (josh-cheek)
on 2012-09-14 11:46
(Received via mailing list)
On Thu, Sep 13, 2012 at 10:00 PM, Matt Neuburg <matt@tidbits.com> wrote:

> Or, to put it a better way, inject abstracts intuitively obvious notions
> such as join, sum, and product. And in Ruby 1.9.3 it gets even cooler,
> since you can just say:
>
>    [1,2,3].inject(:+)
>
>
I disagree. Nearly everything you want to use inject for, 
each_with_object
is better. Array#join, for example is better implemented using
each_with_object than inject

strings.each_with_object("") { |current, joined| joined << current }

Perhaps in a language like Haskell where strings are just lists of
characters, then you could join them in a single pass, if you started at
the end and worked backwards. Or perhaps the thunk could be smart enough 
to
compose or allow access to it without creating all the intermediate 
forms.
But in Ruby

strings.inject("") { |joined, current| joined + current }

is slow and wasteful because it created all the intermediate forms: "",
"a", "ab", "abc", "abcd", "abcde"
Whereas each_with_object only creates: "abcde"



Your example is compelling to you, because you're enamored with the
implementation, but `[1,2,3].inject(:+)` is ugly, confusing, and buggy. 
If
we could say something like `[1,2,3].sum_from 0`, this would be clearer
because we don't care about the implementation. When we see 
`.inject(:+)`
we have to mentally translate it "oh, we're getting the sum". That cost
adds up, things like this should be declarative. It's buggy because if 
your
array is empty, you get `[].inject(:+) # => nil`, but the null object 
for a
sum is 0, so you have to remember to pass the initial argument
`[].inject(0, :+) # => 0` (that param should probably be required). It's
further ugly because it breaks from Ruby conventions. When you have a
method that takes a block, everywhere else you would have to say
`[].inject(0, &:+)`, but here, translating the symbol into a block is 
moved
into the method, causing you to think you have a bug until you go read 
the
docs enough times to remember this one-off case.

And in the few cases where inject does make more sense than
each_with_object, it may be clearer to just iterate over the array with
#each, and update a local variable.



> Not everyone knows LISP or has read The Structure and Interpretation of
> Computer Programs. Those who have, know. m.
>
>
I generally like Lisp, but the elitism around it is just obnoxious, as 
in
this quote (at least for Common Lisp and Scheme, Clojure seems more down 
to
earth).
Posted by botp (Guest)
on 2012-09-14 12:20
(Received via mailing list)
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#+  ?

there is always String#<< or String#concat.

nonetheless, am also a fan of inject syntax, and hope to see someday,
a more super fast version of inject. The inject syntax is stupidly
generic/flexible, that is why many luv it, including me ;-)

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
 ;-)

kind regards -botp
Posted by Josh Cheek (josh-cheek)
on 2012-09-14 13:07
(Received via mailing list)
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"
Posted by Brian Candler (candlerb)
on 2012-09-14 22:40
Josh Cheek wrote in post #1075977:
> But in Ruby
>
> strings.inject("") { |joined, current| joined + current }
>
> is slow and wasteful because it created all the intermediate forms: "",
> "a", "ab", "abc", "abcd", "abcde"
> Whereas each_with_object only creates: "abcde"

Correct, in ruby, because ruby isn't optimised for funtional use. 
Another example is that there is no tail-recursion unless you build ruby 
with a special compile-time option.

But in other languages, what you've written is actually efficient. For 
example, Erlang has a specific optimisation for binaries which are 
appended to, where no reference remains to the previous incarnation of 
the binary.

> Your example is compelling to you, because you're enamored with the
> implementation

I'd say the opposite; in ruby the implementation is not so good, but 
it's the concept, the abstraction, which is attractive.

foo.each => do something for each element of an array
foo.inject => calculate a summary value across all elements of an array

In fully functional languages, foo.each makes no sense because "do 
something" makes no sense - functions don't have side-effects. (Erlang 
is *not* such a language, by the way, but is part way because it doesn't 
have mutable variables).
Posted by Zhi-Qiang Lei (Guest)
on 2012-09-16 06:42
(Received via mailing list)
> Correct, in ruby, because ruby isn't optimised for funtional use.
>
Interesting. Will it be?

Best regards,
Zhi-Qiang Lei
zhiqiang.lei@gmail.com
Posted by Matthew Kerwin (mattyk)
on 2012-09-16 10:00
(Received via mailing list)
On 16 September 2012 14:41, Zhi-Qiang Lei <zhiqiang.lei@gmail.com> 
wrote:
>
> Correct, in ruby, because ruby isn't optimised for funtional use.
>
> Interesting. Will it be?
>

I strongly doubt it, there's too much OO in the core for the language
to ever lean very far in a stateless (functional) direction.

--
  Matthew Kerwin, B.Sc (CompSci) (Hons)
  http://matthew.kerwin.net.au/
  ABN: 59-013-727-651

  "You'll never find a programming language that frees
  you from the burden of clarifying your ideas." - xkcd
Posted by Tony Arcieri (Guest)
on 2012-09-16 19:19
(Received via mailing list)
On Sun, Sep 16, 2012 at 1:00 AM, Matthew Kerwin 
<matthew@kerwin.net.au>wrote:

> I strongly doubt it, there's too much OO in the core for the language
> to ever lean very far in a stateless (functional) direction.


FP and OO are not orthogonal. Perhaps the word you were looking for was
"mutation"
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.