Reputation: 133
I'm just getting started in Ruby, but the manual I'm following seems to be outdated. I've been doing some research but I've been unable to find a clear answer.
The manual used the 'retry' keyword inside of a method that should act as a loop, but it is used without a begin/rescue block, and the behavior seems pretty different when you force the 'retry' keyword to be inside of a begin/rescue block.
I've tried many things: -The first one was using the "begin" keyword at the start of the method and a 'raise' at the retry point, followed by the respective 'rescue; retry; end;'. -The last one was using the 'begin/rescue' block outside, wrapping the call of the method.
Following the logic in the manual, only the last one worked as it should.
There some examples:
The code in the manual is as follows:
def WHILE(cond)
return if not cond
yield
retry
end
i=0; WHILE(i<3) {print i; i+=1}
result: 012
I've tried the following:
def WHILE(cond)
begin
return if not cond
yield
raise
rescue
retry
end
end
i=0; WHILE(i<3) {print i; i+=1}
result: infinite numbers
def WHILE(cond)
return if not cond
yield
end
i=0; begin; WHILE(i<3) {print i; i+=1}; raise; rescue; retry; end
result: 012 (plus an obvious infinite loop printing nothing)
I expect you from taking me out of this existential doubt, but here's MY conclusion.
Before the obligatory use of the begin/rescue block for the 'retry' keyword, it could be used in a way that it can't anymore, repeating the call of a method in spite of being inside of that method.
Now, it just jumps to the 'begin' statement.
But i'm unsure of this, and I need confirmation. And if so, is there any form to recover that kind of use?
Thanks.
Upvotes: 5
Views: 8265
Reputation: 11396
IMHO, using loop
defeats a bit the goal of the given exercise, hence I have two other solutions using redo
or retry
and no loop which I'd like to share.
If you have no idea how I may have done that I suggest you try before looking at the solutions.
retry
def _while(cond, _loop=Class.new(RuntimeError))
raise _loop if cond.call
rescue _loop
yield
retry
end
i = 0
_while(-> { i < 3 }) do
print i
i += 1
end
redo
def _while(cond)
proc do
if cond.call
yield
redo
end
end.call
end
i = 0
_while(-> { i < 3 }) do
print i
i += 1
end
Upvotes: 0
Reputation: 211600
Your WHILE
does not behave like a regular while
since in your case i<3
is evaluated at call time once. The while
statement evaluates it each time.
If you want to write a while
equivalent it's important that your condition be something that can be evaluated, not something that is already evaluated.
You can fix that by accepting a Proc as a condition:
def so_long_as(cond)
loop do
return unless cond.call
yield
end
end
Then you call it like this:
i = 0
so_long_as(-> { i < 3 }) do
print i
i += 1
end
Where that now prints 012
and terminates properly.
What's important to note is that retry
only works within a begin
/end
context, not a regular method, that now you have to use redo
instead:
i = 0
redone = false
so_long_as(-> { i < 3 }) do
print i
unless (redone)
redone = true
redo
end
i += 1
end
Where that prints 0012
now.
There's been some significant changes to how redo
and retry
work that are worth reading up on.
Upvotes: 4