[Facet] Hash#each

D

Daniel Schierbeck

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>
 
T

Trans

Daniel said:
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

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.
 
A

ara.t.howard

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?

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 ;-)

cheers.

-a
 
P

phrogz

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
 
J

James Edward Gray II

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?! ;)

James Edward Gray II
 
D

Daniel Schierbeck

Trans said:
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?

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

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
 
T

Trans

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

ts

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
 
T

Trans

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! ;-)

T.
 
T

Trans

Daniel said:
No, I'm not using it, I'm just reading through some of the Facets source
code :)

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

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.
 
A

ara.t.howard

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}

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
 
A

ara.t.howard

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! ;-)

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
 
S

Sylvain Joyeux

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
 
T

Trans

ts said:
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%

Woh. So why the change?

T.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top