passing a block to yield

A

Andy Bogdanov

Hello

I was trying to make an iterator that goes through a simple tree
structure something like that:

class Node
...
def each_child(&block)
@children.each { |child| yield(child) { child.each_child(&block) } }
end
...
end

so i could generate a simple html menu:

puts "<ul>"
root_node.each_child { |node|
puts "<li><a href=\"#{child.link}\">#{child.title}</a></li>"
unless node.children.empty?
puts "<ul>"
yield
puts "</ul>"
end
}
puts "</ul>"

But it turns out that Ruby syntax prohibits passing blocks to yield. A
simple workaround is to use block.call instead of yield everywhere.

Is there any good reason for that limitation?
 
R

Robert Klemme

I was trying to make an iterator that goes through a simple tree
structure something like that:

class Node
..
def each_child(&block)
@children.each { |child| yield(child) { child.each_child(&block) } }
end
..
end

so i could generate a simple html menu:

puts "<ul>"
root_node.each_child { |node|
puts "<li><a href=\"#{child.link}\">#{child.title}</a></li>"
unless node.children.empty?
puts "<ul>"
yield
puts "</ul>"
end
}
puts "</ul>"

But it turns out that Ruby syntax prohibits passing blocks to yield. A
simple workaround is to use block.call instead of yield everywhere.

Is there any good reason for that limitation?

There is no point in passing a block to yield because yield implicitly
calls the block passed to the current method.

I think what you really want is this:

def each_child(&b)
b[self] # or b.call(self) or simply yield self
@children.each {|ch| ch.each_child(&b)}
self
end

Kind regards

robert
 
A

Andy Bogdanov

There is no point in passing a block to yield

Maybe there always is a way to avoid using procs and lambdas, but i
don't see other easy way in my case (see below).
Proc#call accepts blocks, why does yield not? It's almost the same
thing.
yield implicitly calls the block
passed to the current method.

Thanks for the tip. Forgot that yield belongs to method context only.
def each_child(&b)
b[self] # or b.call(self) or simply yield self
@children.each {|ch| ch.each_child(&b)}
self
end

This code will just pass all the elements to a block, there's no way to
wrap lower levels into <ul> tag. I need to control what happens before
and after each_child so i have use a number of procs as arguments or do
this trick with a block.
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

Hello

I was trying to make an iterator that goes through a simple tree
structure something like that:

class Node
...
def each_child(&block)
@children.each { |child| yield(child) { child.each_child(&block) } }
end
...
end

so i could generate a simple html menu:

puts "<ul>"
root_node.each_child { |node|
puts "<li><a href=\"#{child.link}\">#{child.title}</a></li>"
unless node.children.empty?
puts "<ul>"
yield
puts "</ul>"
end
}
puts "</ul>"

But it turns out that Ruby syntax prohibits passing blocks to yield. A
simple workaround is to use block.call instead of yield everywhere.

Is there any good reason for that limitation?
Looks like you can get it to work by explicitly taking the block, rather
than trying to yield.

def foo(&block)
block.call 1 do
2
end
end

foo do |param,&block|
puts "The param is #{param}"
puts "The block is #{block.call}"
end


I checked it on 1.8.7 - 1.9.2, it does not work on 1.8.6, though.
 
R

Robert Klemme

2010/6/28 Andy Bogdanov said:
There is no point in passing a block to yield

Maybe there always is a way to avoid using procs and lambdas, but i
don't see other easy way in my case (see below).
Proc#call accepts blocks, why does yield not? It's almost the same
thing.
yield implicitly calls the block
passed to the current method.

Thanks for the tip. Forgot that yield belongs to method context only.
def each_child(&b)
=A0 =A0b[self] # or b.call(self) or simply yield self
=A0 [email protected] {|ch| ch.each_child(&b)}
=A0 =A0self
end

This code will just pass all the elements to a block, there's no way to
wrap lower levels into <ul> tag. I need to control what happens before
and after each_child so i have use a number of procs as arguments or do
this trick with a block.

This would not be solved by yield accepting a block however. Your
problem is more complicated. Basically you have a tree structure and
you want to traverse the tree while treating inner nodes and leafs
differently. You would need at least two different blocks to do what
you want. This kind of problem is typically solved with a visitor
pattern [1] or similar solution. Basically you need double dispatch
[2] on the node type and on the algorithm type.

In your case you could do

# untested
Child =3D Struct.new :parent, :info do
def visit(visitor)
visitor.child(self)
end
end

class Parent < Child
def initialize
@children =3D []
end

def visit(visitor)
visitor.enter_node(self)
@children.each {|ch| ch.visit(visitor)}
visitor.leave_node(self)
end
end

class HtmlPrinter
def enter_node(n)
@indent ? @indent + 1 : 1
puts "#{indent * ' '}<ul>"
end

def leave_node(n)
puts "#{indent * ' '}</ul>"
@indent -=3D 1
end

def child(n)
puts "#{indent * ' '} <li>#{n.info}</li>"
end

end

I hope you get the idea.

Kind regards

robert


[1] http://en.wikipedia.org/wiki/Visitor_pattern
[2] http://en.wikipedia.org/wiki/Double_dispatch

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 

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,968
Messages
2,570,153
Members
46,699
Latest member
AnneRosen

Latest Threads

Top