tags:

views:

83

answers:

3

I'd like to write a method that yields values in one place and pass it as a parameter to another method that will invoke it with a block. I'm convinced it can be done but somehow I'm not able to find the right syntax.

Here's some sample (non-working) code to illustrate what I'm trying to achieve:

def yielder
  yield 1
  yield 2
  yield 3
end

def user(block)
  block.call { |x| puts x }
end

# later...
user(&yielder)

$ ruby x.rb
x.rb:2:in `yielder': no block given (yield) (LocalJumpError)
from x.rb:12:in `<main>'

FWIW, in my real code, yielder and user are in different classes.


Update

Thanks for your answers. As Andrew Grimm mentioned, I want the iterator method to take parameters. My original example left this detail out. This snippet provides an iterator that counts up to a given number. To make it work, I made the inner block explicit. It does what I want, but it's a bit ugly. If anyone can improve on this I'd be very interested in seeing how.

def make_iter(upto)
  def iter(upto, block)
    (1 .. upto).each do |v|
      block.call(v)
    end
  end
  lambda { |block| iter(upto, block) }
end

def user(obj)
  obj.call Proc.new { |x| puts x }
end

# later...
user(make_iter(3))
+2  A: 

This doesn't use a lambda or unbound method, but it is the simplest way to go...

def f
  yield 1
  yield 2
end

def g x
  send x do |n|
    p n
  end
end

g :f
DigitalRoss
I like the minimalist approach.
epicsmile
+1  A: 

When you write &yielder, you're calling yielder and then trying to apply the & (convert-to-Proc) operator on the result. Of course, calling yielder without a block is a no-go. What you want is to get a reference to the method itself. Just change that line to user(method :yielder) and it will work.

Chuck
What would you do if `yielder` accepted arguments?
Andrew Grimm
Chuck
@Andrew Grimm: good catch. I did underspecify my requirements. I do indeed want a yielder that is flexible. I don't want user() to have to know about any arguments for yielder() -- the idea is that it's an opaque iterator. I'm thinking I could build a proc with a closure to capture the right values and then pass that.
epicsmile
+1  A: 

I think this might be along the lines of what you want to do:

def yielder
  yield 1
  yield 2
  yield 3
end

def user(meth)
  meth.call { |x| puts x }
end

# later...
user( Object.method(:yielder) )

Some related info here: http://blog.sidu.in/2007/11/ruby-blocks-gotchas.html

Ben Marini