Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a Ruby method recursively from within its associated block. Any other way?

Tags:

ruby

I've come up with this:

def f x, &b
  yield x, b
end
f 4 do |i, b|
  p i
  f i - 1, &b if i > 0
end

Result:

4
3
2
1
0

Is there another way?

like image 713
DigitalRoss Avatar asked Nov 23 '09 23:11

DigitalRoss


2 Answers

It depends upon the particulars of your actual code, but given your example, if you name the block beforehand, you can avoid yielding the value and the block in your function. Eg:

def f(x, &b)
  yield x
end

foo = lambda do |i|
  p i
  f(i-1,&foo) if i > 0
end
f(4,&foo)

However, I'd like to find a more elegant solution to this problem. I suspect this is would be a good application of the Y combinator. As soon as I have something better for you, I'll update this message.

like image 100
Ian Eccles Avatar answered Nov 12 '22 21:11

Ian Eccles


A block can recursively call itself provided it is stored in a variable that is accessible by the block itself. For example:

def f(x)
  block = lambda do |y|
    # some calculation on value, or simply yield to the block passed to f()
    yield y
    block.call(y - 1) if y > 0
  end
  block.call(x)
end

f(4) do |x|
  puts "Yielded block: #{x}"
end

Alternatively, you can return the recursive block, bound to the callers block and then call that block. For example:

def g
  block = lambda do |y|
    # some calculation on value, or simply yield to block passed to g()
    yield y
    block.call(y - 1) if y > 0
  end
end

printing_descender = g do |x|
  puts "Encapsulated block: #{x}"
end
printing_descender.call(4)

Outputs:

Yielded block: 4
Yielded block: 3
Yielded block: 2
Yielded block: 1
Yielded block: 0
Encapsulated block: 4
Encapsulated block: 3
Encapsulated block: 2
Encapsulated block: 1
Encapsulated block: 0
like image 29
Matt Connolly Avatar answered Nov 12 '22 21:11

Matt Connolly