Writing Good #each Methods?

Discussion in 'Ruby' started by James Herdman, Mar 14, 2006.

  1. I'm working my way through The Ruby Course slides at the moment and am
    on the section about dynamicity. Specifically I'm on slide 59 right
    now, the slide about a ListItem -- essentially a link list.

    So, this got me thinking: what makes a good #each method? I'd love to
    hear any pearls of wisdom if any of you have the time.

    Thank you,

    James H.
    James Herdman, Mar 14, 2006
    #1
    1. Advertising

  2. > So, this got me thinking: what makes a good #each method? I'd love to
    > hear any pearls of wisdom if any of you have the time.


    I think an each method should have the following properties
    * All elements must be yielded
    * Each element must be yielded exactly once
    * If the container has some order associated with it (ie Array or
    SortedSet) the elements should be yielded in order
    * If there is more than ordering (such as a post, pre or in order
    walk round a binary tree) then the elements should be yielded
    according to the ordering that is as closest to 'natural' ordering
    (inorder in the case of a binary tree). each methods for the other
    orderings (each_preorder, each_postorder) must also be provided.

    That's my opinion, I'm sure other people might disagree with me here.

    Farrel
    Farrel Lifson, Mar 14, 2006
    #2
    1. Advertising

  3. James Herdman

    Paul Battley Guest

    I'd also add
    * If appropriate, inspect the arity of the block and provide the
    requested number of parameters.

    Hash#each's behaviour is a good example:
    hash =3D {:a=3D>1, :b=3D>2}
    hash.each { |x| p x }
    # [:b, 2]
    # [:a, 1]
    hash.each { |k,v| puts "#{k.inspect} =3D> #{v.inspect}" }
    # :b =3D> 2
    # :a =3D> 1

    Paul.
    Paul Battley, Mar 14, 2006
    #3
  4. Paul Battley wrote:
    > I'd also add
    > * If appropriate, inspect the arity of the block and provide the
    > requested number of parameters.


    And another one

    * return self

    robert
    Robert Klemme, Mar 14, 2006
    #4
  5. James Herdman

    George Ogata Guest

    "Paul Battley" <> writes:

    > I'd also add
    > * If appropriate, inspect the arity of the block and provide the
    > requested number of parameters.
    >
    > Hash#each's behaviour is a good example:
    > hash = {:a=>1, :b=>2}
    > hash.each { |x| p x }
    > # [:b, 2]
    > # [:a, 1]
    > hash.each { |k,v| puts "#{k.inspect} => #{v.inspect}" }
    > # :b => 2
    > # :a => 1


    You don't need to -- just yield the array.

    irb(main):001:0> class C
    irb(main):002:1> def each
    irb(main):003:2> yield [1,2]
    irb(main):004:2> yield [3,4]
    irb(main):005:2> end
    irb(main):006:1> end
    => nil
    irb(main):007:0> C.new.each{|a| p a}
    [1, 2]
    [3, 4]
    => nil
    irb(main):008:0> C.new.each{|a,b| p [a,b]}
    [1, 2]
    [3, 4]
    => nil
    George Ogata, Mar 14, 2006
    #5
  6. James Herdman

    Paul Battley Guest

    > You don't need to -- just yield the array.

    Oops. You are, of course, correct. If it is just an array, that
    behaviour comes automatically.

    Wouldn't it be nice if inject did the same, though? E.g.
    hash.inject([]){ |acc, key, value| ... }

    Paul.
    Paul Battley, Mar 14, 2006
    #6
  7. James Herdman

    George Ogata Guest

    "Paul Battley" <> writes:

    > Wouldn't it be nice if inject did the same, though? E.g.
    > hash.inject([]){ |acc, key, value| ... }


    You can come close:

    hash.inject([]){ |acc, (key, value)| ... }
    George Ogata, Mar 14, 2006
    #7
  8. James Herdman

    Pit Capitain Guest

    Paul Battley schrieb:
    > Wouldn't it be nice if inject did the same, though? E.g.
    > hash.inject([]){ |acc, key, value| ... }


    Paul, you have to use parentheses:

    hash.inject([]){ |acc, (key, value)| ... }

    Regards,
    Pit
    Pit Capitain, Mar 14, 2006
    #8
  9. James Herdman

    Paul Battley Guest

    > hash.inject([]){ |acc, (key, value)| ... }

    Thanks! I hadn't thought to try that before. It's definitely going to
    make my future use of inject more concise and readable.

    Paul.
    Paul Battley, Mar 14, 2006
    #9
  10. James Herdman

    James H. Guest

    Concerning ordered elements, what is the best way to ascertain the
    order wherein the structure is not an array? For instance, suppose you
    had a link list. You *could* have a class variable that kept track of
    the beginning, but this would mean difficulties when you had more than
    one link list in play.

    In trying to solve the order issue (naively), I wrote a method like:

    def each(&block)
    current = self.previous
    until current.previous.nil?
    current = current.previous
    end

    # ...
    end

    This obviously doesn't work because on each call of #each, the focus is
    rewound to the intial element in the list.

    Any advice on the matter?
    James H., Mar 14, 2006
    #10
  11. James Herdman

    James H. Guest

    I'm a little confused by this. As I understand it, "arity" is the
    number of arguments to a term. Perhaps I'm just not seeing it in your
    example, but where does arity fit into it? Doubly, why might one want
    to know this?
    James H., Mar 14, 2006
    #11
  12. --Apple-Mail-4-841261084
    Content-Transfer-Encoding: 7bit
    Content-Type: text/plain;
    charset=US-ASCII;
    delsp=yes;
    format=flowed


    On Mar 14, 2006, at 4:05 AM, Paul Battley wrote:

    > I'd also add
    > * If appropriate, inspect the arity of the block and provide the
    > requested number of parameters.
    >
    > Hash#each's behaviour is a good example:
    > hash = {:a=>1, :b=>2}
    > hash.each { |x| p x }
    > # [:b, 2]
    > # [:a, 1]
    > hash.each { |k,v| puts "#{k.inspect} => #{v.inspect}" }
    > # :b => 2
    > # :a => 1
    >
    > Paul.


    This isn't really a function of Hash#each being smart...

    irb(main):001:0> def example()
    irb(main):002:1> yield [:a, 1]
    irb(main):003:1> end
    => nil
    irb(main):004:0> example { |x| p x }
    [:a, 1]
    => nil
    irb(main):005:0> example { |k, v| p k; p v; }
    :a
    1
    => nil

    As you can see yielding an array automatically gets you this kind of
    behavior. There are downsides of course...
    --Apple-Mail-4-841261084--
    Logan Capaldo, Mar 14, 2006
    #12
  13. James Herdman

    James H. Guest

    James H., Mar 14, 2006
    #13
    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. John Blair
    Replies:
    1
    Views:
    408
    Eliyahu Goldin
    Aug 3, 2005
  2. Tjerk Wolterink

    xsl:for-each for each 3 elements problem

    Tjerk Wolterink, Nov 3, 2004, in forum: XML
    Replies:
    3
    Views:
    425
    Tjerk Wolterink
    Nov 3, 2004
  3. Pat Maddox
    Replies:
    6
    Views:
    151
    Marcin Mielżyński
    Jan 20, 2006
  4. Kenneth McDonald
    Replies:
    5
    Views:
    312
    Kenneth McDonald
    Sep 26, 2008
  5. Igor Nn
    Replies:
    7
    Views:
    435
    Johnny Morrice
    May 28, 2011
Loading...

Share This Page