On Nov 11, 2007 1:39 PM, Trans [email protected] wrote:
As Robert K. points out, it breaks depending on what you are
[1,2,3].inject({}) { |memo,n| memo[n] = n ; memo} # => {1=>1, 2=>2, 3=>3}
‘injecting’ without understanding it might run into things like this.
Hmmm… does this mean that the same effect can be achieved with:
module Enumerable
def inject!(obj)
each { |var| yield(obj, var) }
obj
end
end
If you mean the same effect as the current inject. NO. This fails for
the case I cited:
[1,2,3].inject(0) { |memo,n| memo + n} # => 6
which with the definition above would return 0.
This is an example of the ‘normal’ use of inject:
qri Enumerable#inject
------------------------------------------------------ Enumerable#inject
enum.inject(initial) {| memo, obj | block } => obj
enum.inject {| memo, obj | block } => obj
Combines the elements of enum by applying the block to an
accumulator value (memo) and each element in turn. At each step,
memo is set to the value returned by the block. The first form
lets you supply an initial value for memo. The second form uses
the first element of the collection as a the initial value (and
skips that element while iterating).
# Sum some numbers
(5..10).inject {|sum, n| sum + n } #=> 45
# Multiply some numbers
(5..10).inject(1) {|product, n| product * n } #=> 151200
# find the longest word
longest = %w{ cat sheep bear }.inject do |memo,word|
memo.length > word.length ? memo : word
end
longest #=> "sheep"
# find the length of the longest word
longest = %w{ cat sheep bear }.inject(0) do |memo,word|
memo >= word.length ? memo : word.length
end
longest #=> 5
The redefinition would change the results the last example to return 0
instead of 5. And it would make the other ones illegal since it
requires the initial parameter.
Now I noticed that you picked up David Black’s suggestion of inject!
instead of inject, but I took the that he added as an indication
that it was tongue in cheek.
The main point here is that Enumerable#inject as it’s defined does not
rely on side effects in the block to change the memo object.
I think the common usecase is simply not having to create the initial
object outside the loop statement, eg.
obj = 0
each { |i| … obj … }
If I may be so bold as to suggest that injecting as posited by Ezra
might really be a result of cargo culting the “k combinator”
represented by Object#returning with a slightly less than perfect
understanding of Enumerable#inject.
His motivating example:
[1,2,3].inject({}) { |memo,n| memo[n] = n ; memo} # => {1=>1,
2=>2, 3=>3}
Is an unusual use of inject. It might just as well be written as:
h = {}; [1, 2, 3].each {|i| h[i] = i}; h
Which admittedly is uglier due to the need to use the temporary h, and
rename it. But here’s an alternative way to accomplish Ezra’s use case
using Object#returning (which is implemented as part of the
ActiveSupport gem):
This is the definition of Object#returning from ActiveSupport
class Object
def returning(value)
yield(value)
value
end
end
Ezra’s example of injecting was:
[1,2,3].injecting({}) { |memo,n| memo[n] = n } # => {1=>1, 2=>2, 3=>3}
The same thing using returning
returning({}) {|hsh| [1,2,3].each {|i| hsh[i] = i}} # => {1=>1, 2=>2,
3=>3}
–
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/