Forum: Ruby Writing Good #each Methods?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
James H. (Guest)
on 2006-03-14 07:57
(Received via mailing list)
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.
Farrel L. (Guest)
on 2006-03-14 09:18
(Received via mailing 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.

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
Paul B. (Guest)
on 2006-03-14 11:06
(Received via mailing list)
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.
Robert K. (Guest)
on 2006-03-14 11:45
(Received via mailing list)
Paul B. 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
George O. (Guest)
on 2006-03-14 12:19
(Received via mailing list)
"Paul B." <removed_email_address@domain.invalid> writes:

> # :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
Paul B. (Guest)
on 2006-03-14 12:37
(Received via mailing list)
> 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.
Pit C. (Guest)
on 2006-03-14 12:55
(Received via mailing list)
Paul B. 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
George O. (Guest)
on 2006-03-14 12:55
(Received via mailing list)
"Paul B." <removed_email_address@domain.invalid> 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)| ... }
Paul B. (Guest)
on 2006-03-14 13:05
(Received via mailing list)
>   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.
James H. (Guest)
on 2006-03-14 18:04
(Received via mailing list)
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. (Guest)
on 2006-03-14 18:07
(Received via mailing list)
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?
Logan C. (Guest)
on 2006-03-14 19:29
(Received via mailing list)
On Mar 14, 2006, at 4:05 AM, Paul B. wrote:

> # :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...
James H. (Guest)
on 2006-03-14 22:54
(Received via mailing list)
I've had a few people write me asking about the Ruby Course slides.
Not that I mind replying to each of you, but I thought I'd save the
curious a little time =)

All sorts of Ruby articles and documents can be found at the Ruby Doc
website: http://www.ruby-doc.org

The slides in particular are found here (NB this is a direct link to a
PDF and may at some point change):
http://www.ruby-doc.org/docs/Immersive%20Ruby%20pr...

Enjoy!

James H.
This topic is locked and can not be replied to.