Equivalent injecting implementations?

Are these strictly equivalent? I get the feeling no, but I haven’t
been able to demonstrate it yet.

def injecting(s)
inject(s) do |k, i|
yield(k, i); k
end
end

def injecting(res, &block)
([res]*length).zip(to_a).each(&block)
res
end

Thanks,
T.

On 11/9/07, Trans [email protected] wrote:

([res]*length).zip(to_a).each(&block)
res

end

Thanks,
T.

I can’t see how in general they can be.

In which class or module are you defining these? Since you are using
inject and each, I’m guessing that it’s either Enumerable or some
subclass,

But then depending on which subclass you don’t have a length method
so…

Perhaps some more context would help.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Nov 10, 8:50 am, “Rick DeNatale” [email protected] wrote:

end

I can’t see how in general they can be.

In which class or module are you defining these? Since you are using
inject and each, I’m guessing that it’s either Enumerable or some
subclass,

But then depending on which subclass you don’t have a length method so…

Perhaps some more context would help.

Oh sorry, I meant to put that. These are Enumerable methods.

T.

On Nov 10, 2007 9:37 AM, Trans [email protected] wrote:

def injecting(s)
Thanks,
Perhaps some more context would help.

Oh sorry, I meant to put that. These are Enumerable methods.

module Enumerable
def injecting(s)
inject(s) do |k, i|
yield(k, i); k
end
end

def injecting2(res, &block)
([res]*length).zip(to_a).each(&block)
res
end
end

(1…3).injecting(0) {|k,i| k + i} # => 0
(1…3).injecting2(0) {|k,i| k + i} # =>

~> -:9:in injecting2': undefined local variable or method length’

for 1…3:Range (NameError)

~> from -:15

Which illustrates my point.

Just what is injecting supposed to mean anyway? The first
implementation uses inject and takes a two argument block like inject
does, but seems to be going out of it’s way to disregard the value of
the argument block.

This doesn’t seem much like inject to me.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Nov 10, 2007, at 7:50 AM, Rick DeNatale wrote:

But then depending on which subclass you don’t have a length
end

~> -:9:in injecting2': undefined local variable or methodlength’

This doesn’t seem much like inject to me.
injecting is similar to returning in rails. There is a common pattern
of accumulating a hash via inject that looks like this:

[1,2,3].inject({}) { |memo,n| memo[n] = n ; memo}
#=> {1=>1, 3=>3,2=>2}

Note the ugly trailing ;memo, this is so the fll accumulated hash is
the final return result.

injecting is just a little bit nicer way to do the same thing:

[1,2,3].injecting({}) { |memo,n| memo[n] = n }
#=> {1=>1, 3=>3,2=>2}

Cheers-
-Ezra

On 10.11.2007 16:50, Rick DeNatale wrote:

inject(s) do |k, i|

def injecting(s)

does, but seems to be going out of it’s way to disregard the value of
the argument block.

This doesn’t seem much like inject to me.

Even if you do use it on an Array the two are not equivalent since the
second variant does not propagate the block result. The second variant
works only when you only modify something (say a Hash).

I don’t see the point in having the first one since it reimplements
#inject - but without the subtle specialty of different behavior when no
args are passed to it.

Kind regards

robert

On Nov 10, 11:45 am, Robert K. [email protected] wrote:

Even if you do use it on an Array the two are not equivalent since the
second variant does not propagate the block result. The second variant
works only when you only modify something (say a Hash).

Ah, that’s what I was wondering about. It has to be inplace
modifications to get the same output. Ok.Thanks.

I don’t see the point in having the first one since it reimplements
#inject - but without the subtle specialty of different behavior when no
args are passed to it.

Yea. it’s minor, but some people like it. Actually just using #each is
much faster than inject or injecting. So speed freaks, forget that
silly inject!

T.

On Nov 10, 10:50 am, “Rick DeNatale” [email protected] wrote:

def injecting(s)
Thanks,
Perhaps some more context would help.
def injecting2(res, &block)

Which illustrates my point.

Ah. sorry. I missed what you were getting at there. Yea, right off the
bat length isn’t support, so it would have to apply to Array instead.
I was really just curious how they differ in result because they were
outputing the same thing when I rand some cases on it.

Just what is injecting supposed to mean anyway? The first
implementation uses inject and takes a two argument block like inject
does, but seems to be going out of it’s way to disregard the value of
the argument block.

This doesn’t seem much like inject to me.

It’s basically the the same, but you don’t need to pass the memo back
each time.

T.

Hi –

On Sun, 11 Nov 2007, Rick DeNatale wrote:

So while the idea of an injecting method seems nice, I’m not enamored
of the name.

How about inject! ? :slight_smile:

David

On Nov 10, 2007 4:23 PM, Ezra Z. [email protected] wrote:

On Nov 10, 2007, at 7:50 AM, Rick DeNatale wrote:

Just what is injecting supposed to mean anyway? The first
implementation uses inject and takes a two argument block like inject
does, but seems to be going out of it’s way to disregard the value of
the argument block.

This doesn’t seem much like inject to me.

injecting is similar to returning in rails.

[1,2,3].injecting({}) { |memo,n| memo[n] = n }
#=> {1=>1, 3=>3,2=>2}

Yes, I suspected that this was what Trans was trying to do, but since
the second proposed ‘maybe equivalent’ implementation didn’t seem to
be doing the same thing, I was probing for what he really wanted.

Having said that, and played a bit with the concept. I’m not sure how
useful an injecting method (at least with that name is).

As Robert K. points out, it breaks depending on what you are
injecting. And it’s subject to “cargo culting.”

module Enumerable
def injecting(obj, &blk)
inject(obj) { |mem, var| yield(mem, var); mem }
end
end

As Ezra points out:

[1,2,3].inject({}) { |memo,n| memo[n] = n ; memo} # => {1=>1, 2=>2,
3=>3}
[1,2,3].injecting({}) { |memo,n| memo[n] = n} # => {1=>1, 2=>2,
3=>3}

But this only works because the block has a side-effect on the memo
object. And yes, in cases like this each instead of inject will have
the same side effects.

[1,2,3].inject(0) { |memo,n| memo + n} # => 6
[1,2,3].injecting(0) { |memo,n| memo + n} # => 0

Here’s where the cargo cult danger comes in. Someone who comes across
‘injecting’ without understanding it might run into things like this.

So while the idea of an injecting method seems nice, I’m not enamored
of the name.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Nov 11, 9:50 am, “Rick DeNatale” [email protected] wrote:

module Enumerable
But this only works because the block has a side-effect on the memo
object. And yes, in cases like this each instead of inject will have
the same side effects.

[1,2,3].inject(0) { |memo,n| memo + n} # => 6
[1,2,3].injecting(0) { |memo,n| memo + n} # => 0

Here’s where the cargo cult danger comes in. Someone who comes across
‘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

I think the common usecase is simply not having to create the initial
object outside the loop statement, eg.

obj = 0
each { |i| … obj … }

so that would make sense.

T.

On Nov 10, 1:23 pm, Ezra Z. [email protected] wrote:

injecting is similar to returning in rails. There is a common pattern
of accumulating a hash via inject that looks like this:

[1,2,3].inject({}) { |memo,n| memo[n] = n ; memo}
#=> {1=>1, 3=>3,2=>2}

Note the ugly trailing ;memo, this is so the fll accumulated hash is
the final return result.

Note that you can avoid the trailing memo with this:
[1,2,3].inject({}) { |memo,n| memo.update n=>n }
…but it’s a good deal slower:

a = (1…10_000).to_a
N = 500

require ‘benchmark’
Benchmark.bmbm{ |x|
x.report( ‘set’ ){
N.times{
a.inject({}){ |memo,n| memo[n] = n; memo }
}
}
x.report( ‘update’ ){
N.times{
a.inject({}){ |memo,n| memo.update n=>n }
}
}
}

Rehearsal ------------------------------------------
set 5.860000 0.010000 5.870000 ( 5.981301)
update 11.830000 0.020000 11.850000 ( 11.865100)
-------------------------------- total: 17.720000sec

           user     system      total        real

set 5.960000 0.040000 6.000000 ( 6.011882)
update 11.850000 0.070000 11.920000 ( 11.925828)

On Nov 11, 6:15 pm, “Rick DeNatale” [email protected] wrote:

end
[1,2,3].inject(0) { |memo,n| memo + n} # => 6
obj
end
end

If you mean the same effect as the current inject. NO. This fails for
the case I cited:

No. That’s not what I meant. I realize the difference between inject
and injecting. I’m interested in the implementations of injecting.

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.

You misunderstand me. I have no intention of that. inject and
injecting are different.

Now I noticed that you picked up David Black’s suggestion of inject!
instead of inject, but I took the :slight_smile: that he added as an indication
that it was tongue in cheek.

Joke or not it seems appropriate.‘!’ often in indicates in place
action.

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}

I see the formal equivalence. But if I ever used that, please take my
keyboard away.

T.

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 :slight_smile: 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/