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?
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
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?!
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
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).
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!
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!
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"
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