[Facet] Hash#each

Discussion in 'Ruby' started by Daniel Schierbeck, Aug 15, 2006.

  1. 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, Aug 15, 2006
    #1
    1. Advertising

  2. Daniel Schierbeck

    Trans Guest

    Re: Hash#each

    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.
     
    Trans, Aug 15, 2006
    #2
    1. Advertising

  3. Daniel Schierbeck

    Guest

    Re: Hash#each

    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?


    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
    --
    to foster inner awareness, introspection, and reasoning is more efficient than
    meditation and prayer.
    - h.h. the 14th dali lama
     
    , Aug 15, 2006
    #3
  4. Daniel Schierbeck

    Guest

    Re: Hash#each

    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
     
    , Aug 15, 2006
    #4
  5. Re: Hash#each

    On Aug 15, 2006, at 10:30 AM, 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?! ;)

    James Edward Gray II
     
    James Edward Gray II, Aug 15, 2006
    #5
  6. Re: Hash#each

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

    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
     
    Daniel Schierbeck, Aug 15, 2006
    #6
  7. Daniel Schierbeck

    Trans Guest

    Re: Hash#each

    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.
     
    Trans, Aug 15, 2006
    #7
  8. Daniel Schierbeck

    ts Guest

    Re: Hash#each

    >>>>> "T" == Trans <> 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
     
    ts, Aug 15, 2006
    #8
  9. Daniel Schierbeck

    Trans Guest

    Re: Hash#each

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

    T.
     
    Trans, Aug 15, 2006
    #9
  10. Daniel Schierbeck

    Trans Guest

    Re: Hash#each

    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 :)
    >
    > 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.
     
    Trans, Aug 15, 2006
    #10
  11. Daniel Schierbeck

    Guest

    Re: Hash#each

    On Wed, 16 Aug 2006, Trans wrote:

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


    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
    --
    to foster inner awareness, introspection, and reasoning is more efficient than
    meditation and prayer.
    - h.h. the 14th dali lama
     
    , Aug 15, 2006
    #11
  12. Daniel Schierbeck

    Guest

    Re: Hash#each

    On Wed, 16 Aug 2006, Trans wrote:

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


    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
    --
    to foster inner awareness, introspection, and reasoning is more efficient than
    meditation and prayer.
    - h.h. the 14th dali lama
     
    , Aug 15, 2006
    #12
  13. 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
     
    Sylvain Joyeux, Aug 15, 2006
    #13
  14. Daniel Schierbeck

    Trans Guest

    Re: Hash#each

    ts wrote:
    > >>>>> "T" == Trans <> 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%


    Woh. So why the change?

    T.
     
    Trans, Aug 16, 2006
    #14
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jean-Marie Condom
    Replies:
    0
    Views:
    685
    Jean-Marie Condom
    Dec 16, 2004
  2. Dj Frenzy
    Replies:
    0
    Views:
    447
    Dj Frenzy
    Dec 16, 2004
  3. FC
    Replies:
    4
    Views:
    5,853
    Keith Davies
    Oct 27, 2003
  4. rp
    Replies:
    1
    Views:
    538
    red floyd
    Nov 10, 2011
  5. Igor Nn
    Replies:
    7
    Views:
    437
    Johnny Morrice
    May 28, 2011
Loading...

Share This Page