Passing a block to yield

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 “


    root_node.each_child { |node|
    puts “
  • <a href=”#{child.link}">#{child.title}
  • "
    unless node.children.empty?
    puts “

      yield
      puts “

    end
    }
    puts “

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?

On 28.06.2010 14:52, Andy B. wrote:

}
puts “”

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

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

    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.

On Mon, Jun 28, 2010 at 7:52 AM, Andy B.
[email protected]wrote:


puts “”

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.

2010/6/28 Andy B. [email protected]:

Thanks for the tip. Forgot that yield belongs to method context only.
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 = Struct.new :parent, :info do
def visit(visitor)
visitor.child(self)
end
end

class Parent < Child
def initialize
@children = []
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 * ’ '}


    end

    def leave_node(n)
    puts “#{indent * ’ '}


@indent -= 1
end

def child(n)
puts “#{indent * ’ '}

  • #{n.info}

  • end

    end

    I hope you get the idea.

    Kind regards

    robert

    [1] http://en.wikipedia.org/wiki/Visitor_pattern
    [2] Double dispatch - Wikipedia