[Facet] 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 Schierbeck wrote:

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.

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
--
Sylvain Joyeux

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]]
     :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 Wed, 16 Aug 2006, Trans wrote:

   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?

--
to foster inner awareness, introspection, and reasoning is more efficient than
meditation and prayer.
- h.h. the 14th dali lama

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

Trans wrote:

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

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

James Edward Gray II

···

On Aug 15, 2006, at 10:30 AM, phrogz@gmail.com 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

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.

···

ara.t.howard@noaa.gov wrote:

both will cause all sorts of issues. this fails:

phrogz@gmail.com 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.

Daniel Schierbeck wrote:

Trans wrote:
> 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 :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

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.

irb(main):002:0> h.each { |*v| p v }
[[:b, 2]]
[[: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%

irb(main):003:0> require 'facet/hash/each'
=> true
irb(main):004:0> h.each { |*v| p v }
[:b, 2]
[: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

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

···

On Wed, 16 Aug 2006, Trans wrote:

ara.t.howard@noaa.gov wrote:

--
to foster inner awareness, introspection, and reasoning is more efficient than
meditation and prayer.
- h.h. the 14th dali lama

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 Wed, 16 Aug 2006, Trans wrote:

phrogz@gmail.com 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:

--
to foster inner awareness, introspection, and reasoning is more efficient than
meditation and prayer.
- h.h. the 14th dali lama

ts wrote:

> irb(main):002:0> h.each { |*v| p v }
> [[:b, 2]]
> [[: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%

> irb(main):003:0> require 'facet/hash/each'
> => true
> irb(main):004:0> h.each { |*v| p v }
> [:b, 2]
> [: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.