Ryan Davis
8/7/2006 8:50:00 AM
On Aug 7, 2006, at 12:36 AM, Peña, Botp wrote:
> Hi All,
>
> inject is a powerful method in ruby. but the ff gives me surprise..
>
> irb(main):001:0> sum=0
> => 0
> irb(main):002:0> [1,2,3,4,5].inject{|sum,e| sum+e }
> => 15
> irb(main):003:0> sum
> => 10
Inject is just an iterator:
% echo "sum=0; [1,2,3,4,5].inject{|sum,e| sum+e }" | parse_tree_show -f
[[:lasgn, :sum, [:lit, 0]],
[:iter,
[:call,
[:array, [:lit, 1], [:lit, 2], [:lit, 3], [:lit, 4], [:lit, 5]],
:inject],
[:masgn,
[:array,
[:lasgn, :sum], # <<<<<
[:dasgn_curr, :e]]],
[:call, [:lvar, :sum], :+, [:array, [:dvar, :e]]]]]
Because sum was assigned initially outside of the inject, it is an
lvar (local variable) instead of a dvar (dynamic/iter var, like e).
You'll notice that the assignments to both of those variables happens
before the call, and is part of the iteration mechanics itself. There
is no assignment done at the end of an iteration.
I think it is more important to point out that inject is
_just_another_iterator_. There is nothing special about it or how it
works. It is just a simple each just like everything else in
Enumerable. This makes the ruby implementation cleaner and easier to
maintain. Sum isn't an accumulator, as much as it is just another
variable. Should map or reject have some special semantics attached
to their block variables?
If you really are stuck on this idea, you can always change "sum+e"
to read "sum+=e" but at that stage, why use an inject at all?
Here is our implementation from metaruby:
def inject(memo = :_nothing)
enum = self.to_a.dup
memo = enum.shift if memo == :_nothing
return memo if enum.empty?
enum.each do |item|
memo = yield memo, item
end
return memo
end