Hash#each

The current implementation of Hash#each[1]:

File lib/facets/core/hash/each.rb, line 19

def each(&yld)
case yld.arity
when 0
when 1
each_value{|v| yield(v)}
else
each_pair{|k,v| yld.call(k,v)}
end
self
end

To me it looks like we’re creating more Proc objects than necessary.
Would this not suffice?

def each(&block)
if block.arity < 2
each_value(&block)
else
each_pair(&block)
end
end

Cheers,
Daniel

[1] http://facets.rubyforge.org/api/core/classes/Hash.html#M000153

Daniel S. wrote:

 end
   each_pair(&block)
 end

end

Yes that’s much better.

On the other hand, I’m glad you bring this up. Are you actively using
this call? As you hopefully noticed from the docs, this variation of
Hash#each comes with a WARNING:

WARNING! Use with caution. Methods from other libraries

may depend on the old behavior, expecting a two element

array to be passed into a single block argument.

I’m “abstractly” of the opinion that this alternate definition makes
more sense, nonetheless it may just be TOO danagerous for practicel use
b/c of the compatability issue. Would others concur? Or is it safe to
use in limited circumstance as I have been assuming?

T.

On Wed, 16 Aug 2006, Trans wrote:

On the other hand, I’m glad you bring this up. Are you actively using
use in limited circumstance as I have been assuming?
both will cause all sorts of issues. this fails:

 harp:~ > cat a.rb
 h = {:k => :v}

 h.each{|*a| p a}

 class << h
   def each(&block)
     if block.arity < 2
       each_value(&block)
     else
       each_pair(&block)
     end
   end
 end

 h.each{|*a| p a}


 harp:~ > ruby a.rb
 [[:k, :v]]
 [:v]

it’s much trickier than you give credit. for instance this also fails:

 harp:~ > cat a.rb
 h = {:k => :v}

 h.each{|*a| p a}

 class Hash
   def each *a, &b
     send "each_#{ b.arity == 1 ? 'value' : 'pair' }", &b
   end
 end

 h.each{|*a| p a}

 h.each{|v| p v}


 harp:~ > ruby a.rb
 [[:k, :v]]
 [:k, :v]
 :v

this looks close, but i’ll leave to someone else to see how it also
might fail:

 harp:~ > cat a.rb
 h = {:k => :v}

 h.each{|*a| p a}

 class Hash
   # cache all original instance methods
   METHODS = Hash.new{|h,k| h[k] = instance_method k}
   instance_methods.each{|m| METHODS[m]}

   def each *a, &b
     b.arity == 1 ? each_value(*a, &b) : 

METHODS[‘each’].bind(self).call(*a, &b)
end
end

 h.each{|*a| p a}

 h.each{|v| p v}


 harp:~ > ruby a.rb
 [[:k, :v]]
 [[:k, :v]]
 :v

you have to understand your arities if you’re going to go that route.
moral:
don’t override built-ins :wink:

cheers.

-a

On Aug 15, 2006, at 10:30 AM, [email protected] wrote:

Is this whole facet just to avoid typing two extra characters in the
case where you don’t care about the keys? Using standard Ruby 1.8.4
without facets:

foo = { :name=>“Gavin”, :age=>33 }
foo.each{ |_,v| p v }
#=> “Gavin”
#=> 33

And why the heck wouldn’t we just use each_value() there?! :wink:

James Edward G. II

Trans wrote:

b/c of the compatability issue. Would others concur? Or is it safe to
use in limited circumstance as I have been assuming?

No, I’m not using it, I’m just reading through some of the Facets source
code :slight_smile:

As Ara pointed out, this may not even work as expected, so perhaps it
would be better to remove it all together.

P.S. I’ve made a few suggestions on the Facets wiki

Cheers,
Daniel

Is this whole facet just to avoid typing two extra characters in the
case where you don’t care about the keys? Using standard Ruby 1.8.4
without facets:

foo = { :name=>“Gavin”, :age=>33 }
foo.each{ |_,v| p v }
#=> “Gavin”
#=> 33

[email protected] wrote:

both will cause all sorts of issues. this fails:

I’m not following how it fails? I may be missing something but it seems
to do what I’d expect:

irb(main):002:0> h.each { |*v| p v }
[[:b, 2]]
[[:a, 1]]
=> {:b=>2, :a=>1}
irb(main):003:0> require ‘facet/hash/each’
=> true
irb(main):004:0> h.each { |*v| p v }
[:b, 2]
[:a, 1]
=> {:b=>2, :a=>1}

It’s not that is that it’s doing something differnet than Ruby normally
does --that’s the whole idea. This is an alternate definition to
Hash#each. (See my next post for the why of it all).

T.

“T” == Trans [email protected] writes:

T> irb(main):002:0> h.each { |*v| p v }
T> [[:b, 2]]
T> [[:a, 1]]

svg% /usr/bin/ruby -ve ‘{:a => 1, :b => 2}.each {|*v| p v}’
ruby 1.8.4 (2005-12-24) [i486-linux]
[[:a, 1]]
[[:b, 2]]
svg%

T> irb(main):003:0> require ‘facet/hash/each’
T> => true
T> irb(main):004:0> h.each { |*v| p v }
T> [:b, 2]
T> [:a, 1]

svg% ./ruby -ve ‘{:a => 1, :b => 2}.each {|*v| p v}’
ruby 1.6.8 (2002-12-24) [i686-linux]
[:a, 1]
[:b, 2]
svg%

Guy Decoux

Daniel S. wrote:

more sense, nonetheless it may just be TOO danagerous for practicel use
P.S. I’ve made a few suggestions on the Facets wiki
Thanks I’ll have a look!

T.

P.S. Sorry for all my typos --I’m so bad about that. I really need to
learn to slow down and review more.

[email protected] wrote:

Is this whole facet just to avoid typing two extra characters in the
case where you don’t care about the keys? Using standard Ruby 1.8.4
without facets:

foo = { :name=>“Gavin”, :age=>33 }
foo.each{ |_,v| p v }
#=> “Gavin”
#=> 33

Actually an interesting question. This is one of the earliest facets in
the library. It came out of a discussion with David Black, Matz and
others about Polymorphic behavior between Array and Hash. If we
considered a Hash’s key analogous to an Array’s index than one case see
how this definition of #each supports that “meshing”, eg.

x = [ :a, :b ]
x.each { |v| p v }
x = { 1 => :a, 2 => :b }
x.each { |v| p v }

See how both array and the hash produce the same result wihtout haveing
to alter the #each statments. You can’t currently do that. So this
hash#each method was created more as an “idealistic” expirement of this
concept, then for practical applicaiton --which explain why it
overrides #each vs. using each_value.

I still think it has merit, but as an extension it does have the
potential of breaking other code. So I probably should get rid of it
–and I’ve known it. But I’ve sort of left it there as a reminder of
this interesting topic --and indeed it worked! :wink:

T.

On Wed, 16 Aug 2006, Trans wrote:

[[:a, 1]]
=> {:b=>2, :a=>1}

h.each{|a| p a.first.size}

irb(main):003:0> require ‘facet/hash/each’
=> true
irb(main):004:0> h.each { |*v| p v }
[:b, 2]
[:a, 1]
=> {:b=>2, :a=>1}

h.each{|a| p a.first.size} #=> NoMethodError

just pointing out it’s not ok to drop in replace with this impl.

cheers.
-a

On Wed, 16 Aug 2006, Trans wrote:

See how both array and the hash produce the same result wihtout haveing
to alter the #each statments. You can’t currently do that. So this
hash#each method was created more as an “idealistic” expirement of this
concept, then for practical applicaiton --which explain why it
overrides #each vs. using each_value.

I still think it has merit, but as an extension it does have the
potential of breaking other code. So I probably should get rid of it
–and I’ve known it. But I’ve sort of left it there as a reminder of
this interesting topic --and indeed it worked! :wink:

i’m totally with the concept of hash/array interchangeability in some
circumstances - but i think going the other way is easier:

 harp:~ > cat a.rb
 require 'rubygems'
 require 'arrayfields'

 (tuple = %w( ara howard 123 34 )).fields = %w( first_name last_name 

ssn age )

 p tuple['first_name']

 p tuple.values_at('ssn', 'age')

 tuple.each_pair{|k,v| p k => v}

 tuple.each{|v| p v}

 p tuple.join(',')



 harp:~ > ruby a.rb
 "ara"
 ["123", "34"]
 {"first_name"=>"ara"}
 {"last_name"=>"howard"}
 {"ssn"=>"123"}
 {"age"=>"34"}
 "ara"
 "howard"
 "123"
 "34"
 "ara,howard,123,43"

food for thought.

cheers.

-a

On a related note, both solution create one Proc (in the block=>Proc
conversion done by &). The only difference is in the number of created
Array objects when the arity is 2 (two by hash elements for the facet
solution, one by element for the new implementation).

test code

require ‘pp’
require ‘utilrb/objectstats’

class Hash
if ARGV.empty?
STDERR.puts “using facet implementation”
# File lib/facets/core/hash/each.rb, line 19
def each(&yld)
case yld.arity
when 0
when 1
each_value{|v| yield(v)}
else
each_pair{|k,v| yld.call(k,v)}
end
self
end
else
STDERR.puts “using new implementation”
def each(&block)
if block.arity < 2
each_value(&block)
else
each_pair(&block)
end
end
end
end

test = { :a => 1, :b => 2, :c => 3 }
pp ObjectStats.profile { test.each { |a| } }
pp ObjectStats.profile { test.each { |a, b| } }

objectstats.rb is available here
http://www.laas.fr/~sjoyeux/darcs/utilrb/lib/utilrb/objectstats.rb

ts wrote:

svg%
[:a, 1]
[:b, 2]
svg%

Woh. So why the change?

T.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs