Writing Good #each Methods?

J

James Herdman

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

Farrel Lifson

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
 
P

Paul Battley

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

Robert Klemme

Paul said:
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
 
G

George Ogata

Paul Battley said:
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
 
P

Paul Battley

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

George Ogata

Paul Battley said:
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)| ... }
 
P

Pit Capitain

Paul said:
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
 
P

Paul Battley

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

James H.

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?
 
J

James H.

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?
 
L

Logan Capaldo

--Apple-Mail-4-841261084
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
delsp=yes;
format=flowed


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

James H.

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

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top