10

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?

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • 1
    I should add that the recursive instances should still have the associated block, in case that wasn't obvious. – DigitalRoss Nov 23 '09 at 23:49

5 Answers5

4

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.

Ian Eccles
  • 7,297
  • 1
  • 17
  • 20
  • That's good, though I was doubtful about lambda for my case because it's about the same number of bytes as def f..end. This became a bit interesting abstractly but it all started with this: http://stackoverflow.com/questions/1766675/code-golf-running-water/1770264#1770264 – DigitalRoss Nov 24 '09 at 02:51
  • Yeah, unfortunately, if it's the least number of characters used that you're after, using lambda's probably isn't going to be very helpful. Even the abbreviated -> syntax found in Ruby 1.9 still probably wouldn't save much, once you include the y-combinator code. If you're unfamiliar with it, or if any other reader is, examples in Ruby can be found here: http://nex-3.com/posts/43-fun-with-the-y-combinator-in-ruby and here: http://www.eecs.harvard.edu/~cduan/technical/ruby/ycombinator.shtml – Ian Eccles Nov 24 '09 at 04:05
  • Still, a very interesting problem. I'll chew on it a bit more before accepting that the named lambda is the shortest way to go. – Ian Eccles Nov 24 '09 at 04:06
2

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
Matt Connolly
  • 9,757
  • 2
  • 65
  • 61
1
def f(x, &b)
  b.call x
  f(x-1,&b) if x>0
end

f(4) do |x|
 p x
end
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
haoqi
  • 71
  • 1
  • 1
0

Matt's answer is good. It's also the only way to implement an immediate return from a deep recursive search. Note that returning from the block actually returns from the invoking function. unwinding all recursive block calls in one fell swoop.

cliffordheath
  • 2,536
  • 15
  • 16
  • This actually depends if you use `lambda` or `proc`. lambda's are anonymous functions that can be returned, proc's execute in the scope of the parent function and return will do as you say. – Matt Connolly Mar 04 '14 at 02:39
0

there are tons of ways to do this using callcc, or catch/throw (which can always return from deep recursive calls). here is non-golfing version that uses thread local vars

def f x, &b
  t = Thread.current
  t[:b] ||= b
  b ||= t[:b]
  b.call(x)
ensure
  t[:b] = nil
end

f 4 do |i|
  p i
  f i - 1 if i > 0
end