What is wrong in the code?
def call_block(n)
if n==1
return 0
elsif n== 2
return 1
else
yield
return call_block(n-1) + call_block(n-2)
end
end
puts call_block(10) {puts "Take this"}
I am trying to use yield to print Take this other than the tenth fibonacci number.
I am getting the error:
in `call_block': no block given (LocalJumpError)
Even the following code throws error:
def call_block(n)
if n==1
yield
return 0
elsif n== 2
yield
return 1
else
yield
return call_block(n-1) + call_block(n-2)
end
end
puts call_block(10) {puts "Take this"}
You might want to use this line, as Adam Vandenberg hints:
return call_block(n-1) { yield } + call_block(n-2) { yield }
First, let's clean that up a bit so that it's easier to see what's going wrong:
def call_block(n)
return 0 if n == 1
return 1 if n == 2
yield
call_block(n-1) + call_block(n-2)
end
puts call_block(10) { puts 'Take this' }
Now let's just trace it through.
We start by calling
call_block(10) { puts 'Take this' }
So, n
is 10
and the block is { puts 'Take this' }. Since n
is neither 1
nor 2
, we arrive at the yield
, which transfers control to the block.
Now we are calling
call_block(n-1)
which is
call_block(9)
Notice that we are not calling it with a block. So, for this new call, n
is 9
and there is no block. Again, we skip the first two lines and come to the yield
.
But there is no block to yield
to, and that's why the code blows up here.
The solution is both obvious and subtle. The obvious part is: the problem is that we are not passing a block, thus the solution is we need to pass the block along. The subtle part is: how do we do that?
The thing that makes Ruby blocks so syntactically lightweight, is that they are anonymous. But if the block doesn't have a name, we cannot refer to it, and if we cannot refer to it, then we cannot pass it along.
The solution to this is to use another construct in Ruby, which is basically a more heavyweight abstraction for the idea of "a chunk of code" than a block: a Proc
.
def call_block(n, blk)
return 0 if n == 1
return 1 if n == 2
blk.()
call_block(n-1, blk) + call_block(n-2, blk)
end
puts call_block(10, ->{ puts 'Take this' })
As you can see, this is a little bit heavier syntactically, but we can give the Proc
a name, and thus pass it along to the recursive calls.
However, this pattern is actually common enough that there is special support in Ruby for it. If you put a &
sigil in front of a parameter name in a parameter list, Ruby will "package up" a block that is passed as an argument into a Proc
object and bind it to that name. And conversely, if you put a &
sigil in front of an argument expression in an argument list, it will "unpack" that Proc
into a block:
def call_block(n, &blk)
return 0 if n == 1
return 1 if n == 2
yield # or `blk.()`, whichever you prefer
call_block(n-1, &blk) + call_block(n-2, &blk)
end
puts call_block(10) { puts 'Take this' }
That's because of the recursive call to the method call_block
without passing in a block. One way of doing it would be:
def call_block(n, &blk)
if n == 1
return 0
elsif n == 2
return 1
else
blk.call()
return call_block(n-1, &blk) + call_block(n-2, &blk)
end
end
puts call_block(4) {puts "Take this"}
EDIT: I must admit that the solution posted by Justice seems more logical.