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

···

--
Posted via http://www.ruby-forum.com/.

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

···

On 28.06.2010 14:52, Andy Bogdanov wrote:

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?

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

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.

···

On Mon, Jun 28, 2010 at 7:52 AM, Andy Bogdanov <andy.bogdanov@gmail.com>wrote:

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?
--
Posted via http://www.ruby-forum.com/\.

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.

···

--
Posted via http://www.ruby-forum.com/\.

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 * ' '}<ul>"
  end

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

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

end

I hope you get the idea.

Kind regards

robert

[1] Visitor pattern - Wikipedia
[2] Double dispatch - Wikipedia

···

2010/6/28 Andy Bogdanov <andy.bogdanov@gmail.com>:

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.

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