I'm trying to work on a simple ruby kata. I don't really know the concept of yield so I don't really understand what this error means:
LocalJumpError: yield called out of block
Essentially I'm just trying to make this these test cases pass with this code:
def compute
return yield
"Do not compute"
end
Test.expect(compute { "Block" }, "Block")
Test.expect(compute, "Do not compute")
The first test case passes but the second doesn't. Shouldn't it pass though. The return yield should stop the function early with the block. If the method call does not have a block then it should go straight towards "Do not compute" Right?
You want to use block_given?
def compute
return yield if block_given?
"Do not compute"
end
yield always expects a block to yield to, so you have to prevent the yield from occurring yourself if you don't have a block. Thankfully, there's the inbuilt method, block_given? that can tell you whether there's been a block provided or not.
Try removing the return statement.
Once you call return, you exits the method, so the "Do not compute" statement is never reached.
Related
I have an enumerator that loops on data from an external API. The API requests that I notify it when I'm done looping. This is pretty easy when the enumerator is allowed to go until it runs out of data:
def api_enum
return enum_for(:api_enum) unless block_given?
loop_on_api_calls { |thing| yield thing }
notify_api_that_i_am_done
end
But what about this case?
api_enum.each do |thing|
do_stuff(thing)
break
end
The break means I'm never going to call notify_api_that_i_am_done. How could I structure this to guarantee that notify_api_that_i_am_done gets called? Is there a good pattern to for this?
You can just call break with an argument:
api_enum.each do |thing|
do_stuff(thing)
break(notify_api_that_i_am_done)
end
In your api_enum method, you can use ensure to always execute cleanup code, regardless of any exception being thrown or the user breaking. This is a good idea to use for any cleanup code since only tht way you can ensure that the it us run even if something raises an exception in the user-supplied block.
This can be used like this:
def api_enum
return enum_for(:api_enum) unless block_given?
loop_on_api_calls { |thing| yield thing }
ensure
notify_api_that_i_am_done
end
With that pattern, you don't need to follow any special call conventions.
I am working on the following problem:
describe "some silly block functions" do
describe "reverser" do
it "reverses the string returned by the default block" do
result = reverser do
"hello"
end
expect(result).to eq("olleh")
end
From my understanding this should reverse a string. My code is as follows:
def reverser
yield "hello"
end
reverser do |i|
puts i.reverse
end
This simply returns "hello". I may be missing some fundamental concepts here about how yield, blocks, and functions all interact. How do I going about doing what I am trying to accomplish?
The answers are good and correct but perhaps it still do not help.
You should start with your spec:
it "reverses the string returned by the default block"
So, it's very clear what your method should do:
def reverser
# should reverse the string returned by the default block
end
Let's now see how to achieve it. Ok, it should reverse something. But what? Let's see:
string returned by the default block
This suggests that we need to execute the default block and get its returned value. Let's see what the docs say:
yield - Called from inside a method body, yields control to the code block (if any) supplied as part of the method call. ... The value of a call to yield is the value of the executed code block.
So, it seems that your method needs to perform a yield. It will execute a block and return the value the block returns. So, just put a yield there.
def reverser
yield
end
If you run your spec, it will complain - you will see that the string is still not reversed. So, that's whats left for your method to do:
def reverser
yield.reverse
end
and that's it.
You need to include the logic of reversing the string in reverser.
def reverser
yield.reverse
end
But why bothering using block anyway? It's much clearer to use a normal parameter.
def reverser(str)
str.reverse
end
reverser('hello') #=> olleh
If you want to put the string to reverse in the block, then you need to get the result of calling the block and reverse it.
def reverser(&block)
block.call.reverse
end
irb(main):009:0> result = reverser do
irb(main):010:1* "hello"
irb(main):011:1> end
=> "olleh"
I know it's been a year but this hasn't been answered right.
def reverser
out = []
yield.split.each{|word| out << word.reverse}
out.join(" ")
end
I'm pretty sure it has to do with scope
I agree with the above responses - they make the most sense. but want to add why your code isn't working and how to fix it:
expect(result).to eq("olleh")
So according to that you want result to return a string. Is it doing that?
puts returns nil. when you have puts at the end of a method - be aware that the method will return nil. It's insidious because sometimes the results are not what is expected.
but you are expecting it to return 'olleh'
get rid of the puts and it should work like you expect (untested)
def reverser
yield "hello"
end
reverser do |i|
i.reverse # NOTE THAT THE PUTS is missing here
end
I think that's what you are looking for.
edit: Please test and let me know because some folks think I have the answer completely wrong! of course you'd not want to rely on the particular block that you are using as a design point, but this should give you an idea of why it wasn't working
Because Ruby relies so heavily on the use of blocks for iteration (while and for loops do exist in Ruby, but they're very rarely used by Ruby developers in practice) Ruby allows some keywords often associated with loops in other languages, such as break, next, and redo, to be used inside of blocks. return in Ruby also returns from the method the block is inside, which is consistent with the behavior of a loop, instead of from the block itself as functional programming languages do. This answer explains those behaviors quite nicely.
Now what I'm wondering is how this behavior works from the block caller's perspective (by block caller, I mean the method the block is being called from, as opposed to the method the block is defined in). For some of these keywords it's pretty self-explanatory (e.g. next simply returns from the block, and redo calls the block again with the same arguments), but for others the exact behavior is unclear to me. Specifically...
When I call break from a block, does execution immediately get handed to the point after the block is defined? If I have some post-iteration cleanup steps to perform from within the iterator method, do they get completely skipped? E.g.:
def iterate
setup
1.upto(5) do |x|
yield x
end
cleanup # Does this get called? If not, how can I ensure that it does?
end
iterate do |x|
puts x # Prints 1
break # Break prevents further looping, somehow
end
# Execution continues here after break
What about with return? I would assume it has similar behavior to that of break in this context (whatever that behavior may be). Is that indeed the case?
What's even more confusing though is that Ruby blocks can be captured as method parameters and converted to Proc objects. These objects can then be called from anywhere, even outside the method that the block was originally defined in:
def get_proc(&block)
block
end
def get_iterator_proc
p = get_proc do |x| # Block is defined here...
puts x
break
end
# ...so `break` makes execution continue here...
do_other_stuff
return p
end
p = get_iterator_proc
p.call # ...even though the block isn't actually called until here?
And again, the same goes for return. What does it actually return from in this context? How do these keywords really work?
def iterate
setup
1.upto(5) do |x|
yield x
end
cleanup # Does this get called? If not, how can I ensure that it does?
end
iterate do |x|
puts x # Prints 1
break # Break prevents further looping, somehow
end
# Execution continues here after break
cleanup # Does this get called?
Yes. However, if there is an exception, then exception handling takes precedence.
cleanup # If not, how can I ensure that it does?
You can use ensure without a rescue, for when there is an exception.
break # Break prevents further looping, somehow
I'd suspect it uses throw as that is how Enumerable works.
What about with return? I would assume it has similar behavior to that of break in this context (whatever that behavior may be). Is that indeed the case?
It depends on the context. You might get a LocalJumpError: unexpected return or it might work as a break
def get_proc(&block)
block
end
def get_iterator_proc
p = get_proc do |x| # Block is defined here...
puts x
break
end
# ...so `break` makes execution continue here...
Effectively, but there is a subtle difference. It is more like your first example.
def get_proc(&block)
block
# ...so `break` makes execution continue here...
end
def get_iterator_proc
p = get_proc do |x| # Block is defined here...
puts x
break
end
# get_proc returned here (after assigning to p)
Remember, get_proc is just a function call with a special syntax to pass functions.
if you had
def get_proc(&block)
block
7
end
Then in get_iterator_proc p would be 7 (And we'd be sad that 7 didn't have a call method )
I recently purchased the book Seven Languages in Seven Weeks and have been reading through the chapter on Ruby. In the section which introduces blocks (page 40), a code sample is given which illustrates the use of blocks for the purpose of conditionally executing something:
in_case_of_emergency do
use_credit_card
panic
end
def in_case_of_emergency
yield if emergency?
end
This code doesn't make much sense to me, and the book doesn't provide much of an explanation. I was wondering if one of you Ruby gurus would mind helping me get my head around this one.
How can you have both a block and a function with the same name? How would you define "emergency?" I can't even create the block in IRB without it complaining:
NoMethodError: undefined method `in_case_of_emergency' for main:Object
from (irb):1
from :0
And how would you invoke this code to demonstrate how it works? Thanks!
First off: the two are in the wrong order. You need to define in_case_of_emergency first.
Second: You don't name blocks; therefore, it is incorrect that there are two things named in_case_of_emergency. One is a function definition, while the second is the function invocation of that same function.
So, step-by-step:
def emergency?
return rand(2) == 0
end
Let's say you have this function that returns true half the time, and false half the time, randomly. (Boy, that's a lot of emergencies!) Then:
def in_case_of_emergency
yield if emergency?
end
This defines a function named in_case_of_emergency. When called, it executes yield if emergency?, which is a statement yield modified by the conditional if. This is syntactic sugar for
if emergency?()
yield
end
Note that Ruby does not require brackets for function invocation, thus we can drop them; and if there is only one statement inside an if, you can write it on the same line as above (obviating the need for an end).
Next, we have a function invocation:
in_case_of_emergency do
use_credit_card
panic
end
This calls the function we just defined, in_case_of_emergency, passing it a block to execute. These are the two statements (use_credit_card, panic) that would be executed by yield - but only if the emergency? evaluates to true.
Does this make more sense now?
that's simple, that is the method:
def in_case_of_emergency
yield if emergency?
end
and that is the call to your method:
in_case_of_emergency do
use_credit_card
panic
end
wheres
do
use_credit_card
panic
end
is an argument. Ruby methods can implicitly take blocks as arguments. What happens is that
yield will execute the block you provided. In your case yield if emergency? means "execute provided block if conditions are met".
Adding to other answers, it may be easier to understand blocks if you drop the yield keyword and treat blocks in methods like the Procs they actually are.
# for testing purposes, let's always have `emergency?` return `true`
def emergency?
true
end
def in_case_of_emergency(&block)
block.call if emergency?
end
in_case_of_emergency do
puts "AHH! Emergency! Help!", "Seriously, I'm freaking out!"
end
See how much easier it is to realize that the block is actually an argument?
I am using ruby 1.8.7.
p = lambda { return 10;}
def lab(block)
puts 'before'
puts block.call
puts 'after'
end
lab p
Above code output is
before
10
after
I refactored same code into this
def lab(&block)
puts 'before'
puts block.call
puts 'after'
end
lab { return 10; }
Now I am getting LocalJumpError: unexpected return.
To me both the code are doing same thing. Yes in the first case I am passing a proc and in the second case I am passing a block. But &block converts that block into proc. So proc.call should behave same.
And yes I have seen this post Using 'return' in a Ruby block
When you pass in the block with &, you're converting it to a proc. The important point is that a proc and a lambda are different (lambda is actually a subclass of proc), specifically in how they deal with return.
So your refactored code is actually the equivalent of:
p = Proc.new { return 10;}
def lab(block)
puts 'before'
puts block.call
puts 'after'
end
lab p
which also generates a LocalJumpError.
Here's why: A proc's return returns from its lexical scope, but a lambda returns to its execution scope. So whereas the lambda returns to lab, the proc passed into it returns to the outer scope in which it was declared. The local jump error means it has nowhere to go, because there's no enclosing function.
The Ruby Programming Language says it best:
Procs have block-like behavior and lambdas have method-like behavior
You just have to keep track of what you're using where. As others have suggested, all you need to do is drop the return from your block, and things will work as intended.
return inside a block will return from the method the block is in, not from the block. To return from the block use next (it's named that way because with iterator-methods like each and map returning from the block basically means jumping to the next iteration of the loop).
Note that when the return value is the last evaluated expression in the block, you don't need any kind of return statement at all, i.e. lab { 10 } will do the same thing.
The {} block includes the context in which it is given, so the return tries to return from the line lab { return 10; }. You can actually make this work (sometimes even in a useful manner) by placing that line inside a method, which will then return (i.e. "after" is not printed).
To return the 10 to block.call, omit the return (or substitute next).
I think you just need to dereference the block before you pass it:
foo = lambda { return 10 }
def trace_block(&fn)
puts 'before calling fn'
puts fn.call
puts 'after caling fn'
end
trace_block(&foo)
Output:
before calling fn
10
after caling fn
More info:
Understanding Ruby Blocks, Procs and Lambdas
Ruby Blocks 101