February 6th, 2009
I spent the first 2+ years in Ruby-land without even knowing about the probably most underused (and underrated) keyword of the language: redo. Even after I came across the first example and liked it immensely, I could not come up with another use for it, so I threw it to the bottom of my toolbox. Then I found another example, and another one - so I came to the conclusion that redo might be a valuable keyword in your Ruby arsenal after all - it is one of those things which you rarely need, but if you need it, it’s a perfect solution which would be cumbersome to replace with other constructs.
Break vs Next vs Redo
I am sure everyone is familiar with redo’s cousins: next and break. While next and break allow you to skip an iteration, resp. skip the rest of the remaining iterations altogether, redo does not skip anything - it ‘restarts’ the same iteration. In other words, while next transfers control to the end of the iteration block, redo ‘jumps’ to the beginning of the block. Let’s see an example, where redo is used to recover from an input error (code from “The Ruby Programming Language”):
- puts "Please enter the first word you can think of"
- words =%w(apple banana cherry)
- response = words.collect do |word|
- print word + "> "
- response = gets.chop
- if response.size == 0
- word.upcase!
- redo
- end
- response
- end
The trick is that whenever the user enters an empty string, the block is restarted with redo, asking the same ‘question’ until the user enters something that makes sense to the system.
A Real Example - Tail Recursion
As of now, Ruby does not support tail recursion optimization, so you either have to write the function in a non-recursive (i.e. iterative) way yourself, or suffer the consequences of a full stack… or use redo!
I recently came across Magnus Holm’s article on this problem. Magnus provides an elegant solution using redo:
- define_method(:acc) do |i, n, result|
- if i == -1
- result
- else
- i, n, result = i - 1, n + result, n
- redo
- end
- end
- def fib(i)
- acc(i, 1, 0)
- end
Magnus even worked around a redo perk: “The redo statement restarts the current iteration of a loop or iterator”. That means he couldn’t define a method with “def”, since that neither involves a loop nor an iterator. Maybe a less esoteric way relying on the same fact would be to use a lambda:
- def fib(i)
- acc = lambda do |i, n, result|
- if i == -1
- result
- else
- i, n, result = i - 1, n + result, n
- redo
- end
- end.call(i, 1, 0)
- end
Golf Time
Here is a Ruby golf riddle, which I have no idea how could be made this short (42 characters) without redo (via coderrr):
"Generate a random string of length 4 or more consisting of only the following set of characters: a-z, A-Z, 0-9"
Here is the winner, Magnus Holm’s solution (The name sounds familiar? Right, the previous example was from him too - he understands the power of redo for sure :-)).
- (0..9).map{rand(?z).chr[/[^_\W]/]||redo}*""
The above solution might not be instantly crystal clear - so let’s break it down a bit:
- rand(?z) is equivalent to rand(122) (but shaves off a character!)
- rand(?z).chr turns the random number into a string (equivalent to its ASCII code, i.e. 65.chr == “A” etc)
- the regexp /[^_\W]/ means the negation of ‘everything but underscore or non-word character (digits and numbers). To put it another way, accept word characters but not an underscore. (\w includes underscore, so we need to get rid of that)
- rand(?z).chr[/[^_\W]/] acts as an identity function for letters and digits, but returns nil for everything else. So “a”[/[^_\W]/] == “a” but “@”[/[^_\W]/] == nil. “_”[/[^_\W]/] also returns nil.
- And now comes the redo trick: rand(?z).chr[/[^_\W]/] || redo will restart the block until a random character/letter is not found! w00t!
While one might argue that golfing examples are contrived by definition, I don’t always agree: the [/[^_\W]/] is a neat trick to get numbers/letters from a string only, as well as the use of redo is valid and non-contrived.
Conclusion
As demonstrated above, redo can be used in a variety of situations to produce an elegant solution that would look much more cumbersome without it. Of course all the usual stuff applies: don’t overuse, abuse etc. etc.
If you have any other interesting cases for redo, leave a comment - I’d love to see some more

February 6th, 2009 at 3:10 pm
Great article on a truly obscure corner of Ruby!
February 6th, 2009 at 3:22 pm
I recently needed to do a complex iterate/delete sequence: given an array of chains, delete all the chains that are contained in other chains. Normally I’d attack this with recursion, but I had just seen the redo tail-recursion article and wondered if it was possible to do using redo.
If you delete an element while iterating, say at step 4, element 5 will move up to step 4’s place. When you iterate to the next number, 5, you will have skipped the element that just moved up to 4. My final code ended up with this nugget in it:
chains.delete_at(i)
chain = chains[i]
redo unless chain.nil?
It works quite nicely.
February 6th, 2009 at 4:48 pm
Your code sample appears to have a minor transcription error. The final character should be replaced with two quotation marks (single or double will work).
February 6th, 2009 at 4:50 pm
Sorry, I meant to call out the “golf” sample in my last comment.
February 6th, 2009 at 4:55 pm
Thanks Jeff, fixed. In the code it was correct btw (two single quotation marks) but the code highlighter decided to mess it up
Replacing the single quotes with double ones helped…
February 6th, 2009 at 5:09 pm
“redo” is another Perlism in Ruby (http://perldoc.perl.org/functions/redo.html).
You don’t see it often used in Perl either… despite it being a bit more powerful (you can redo loops higher up the stack by using “redo LABEL”.
February 6th, 2009 at 5:22 pm
Awesome. I had never heard of redo.
February 6th, 2009 at 5:57 pm
Redo would be about 5x more awesome if it took arguments that would become the new block parameters. The “assign, then redo” idiom is a bit ugly.
Still, a good trick to be aware of.
February 7th, 2009 at 12:17 am
Thank you for this very helpful article!
February 7th, 2009 at 12:42 am
I do in C style languages like
for (var i=1;i<10;i++)
{
snibble();
snout(i);
if(youwantto_redo==true)
{i=i-1;}
}
February 7th, 2009 at 1:33 am
The fib method (both Magnus’s and yours) return wrong results. It gives me fib(10) => 89, but fib(10) == 55. I see this error quite a lot when people try to write Fibonacci number functions, not sure why. Chart with results for verification: http://en.wikipedia.org/wiki/Fibonacci_number
Anyway, Magnus noted that the performance difference between the redo version and the “iterative” version is almost nothing. That’s because the redo version is an iterative solution.. it’s just using a different tactic (a bit like using a while loop to count instead of a for loop.. same result, just some different keywords).
If you strip out all the cruft, you end up with this:
def fib(i)
n, result = 1, 0
while i != -1
i, n, result = i - 1, n + result, n
end
result
end
.. which is clearly iterative (note: I’ve implemented this to match the results of your methods).
While redo is pretty cool, the whole Fibonacci example seems bogus to me. redo is not a third way, in this situation it’s just a crufty way of producing an iterative solution.
February 7th, 2009 at 2:48 am
PeterC: Hehe, it’s so funny when one is blogging about some obscure advanced feature and he doesn’t get fibonacci right
even if its me in this case
(though I just copied Magnus’s solution and wrapped into a lambda - I ran it, but since I don’t know fib(1000) by heart, it was not apparent that it’s not working :-)).
But yeah, I get your point and you are right to some extent, but I still think the example makes sense if you are viewing the progress how we get there - Magnus’s article is much better in this regard, since it tries to simulate tail recursion optimization and arrives at redo. If the original question would have been ‘is it possible to implement fibonacci numbers iteratively’, no one would even think about reaching after redo. However, if the question is to write ‘tail optimizable recursion’ (ok, I dig it that this was kind of a non-sense
I think the solution is valid.
Anyway, AFAIK 1.9 does tail recursion optimization, so it doesn’t matter any more.
February 7th, 2009 at 6:57 am
PeterC: Frankly, I don’t care if it’s broken, that wasn’t my point
From an interpreter’s point of view, you are completely right. That’s why it’s so fast. However, from a user’s POV it’s not an iterative solution. The redo simply says “run this block (aka method) again”; exactly like acc() would do.
February 7th, 2009 at 8:06 am
[..] from a user’s POV it’s not an iterative solution. The redo simply says “run this block (aka method) again” [..]
So redo is being used to implement a loop (i.e. run this [whatever] again). That’s iterative, right?
It’s a kinda cool experiment to do loops that way, but, of course, there are already nicer/faster ways to loop, such as while..end, loop..end, iterators, and so forth. Using redo in this way is a modern equivalent of “goto”.
Or are you suggesting the average coder would look at that implementation with redo and mistakenly believe it was recursive? I’d really like to hope they wouldn’t, but you never know!
Anyway, AFAIK 1.9 does tail recursion optimization, so it doesn’t matter any more.
Unfortunately not. From vm_opts.h on Ruby 1.9.1-p0:
define OPTTAILCALLOPTIMIZATION 0
However, a comment in the source claims you can turn it on where necessary (although it is claimed odd bugs can emerge) using VM::CompileOption, but I am not familiar with this.
February 7th, 2009 at 8:11 am
Redo screams goto.
February 7th, 2009 at 8:53 am
Huy: not at all - following this logic you can say the same about next or break. Yeah, as PeterC stated above, it can be used as a kind of goto in some cases, but in general, that’s not true.
February 7th, 2009 at 12:42 pm
Huy: I agree with peter on this one. All jumps or branches or looping or subroutine/function/method calls and returns (and many other language idioms) can scream “goto,” if you squint hard enough.
The main historically recognized problem with goto was that it was too generic. Language designers come up with richer, more expressive, and more specific ways to indicate the flow of execution, and gotos become unnecessary and counterproductive. Redo is way more specific and expressive than goto, and as peter mentions, is essentially just as specific as next and break.
February 7th, 2009 at 2:17 pm
Also, I wanted to thank Peter Szinek for mentioning this specific Ruby golf problem. I had a great deal of fun playing with it this morning, and posted about it over at coderrr.
February 7th, 2009 at 3:48 pm
JeffMo: Agreed. Methods, conditions, loops, iterators, break, next and redo; they’re all gotos on crack.
February 7th, 2009 at 4:02 pm
JeffMo: Oh yeah, the Ruby golf problem rocks. Only Magnus’s solution rocks even more
February 7th, 2009 at 5:29 pm
Is it like, continue (in C)? If it is, then it’s awesome.
February 7th, 2009 at 7:34 pm
No, it’s better than that
The equivalent of continue is next; there is no redo() equivalent in C.
February 7th, 2009 at 8:09 pm
I think all 3 of these keywords are bad and here’s why: It makes it hard to refactor your code. Look at this pseudo code:
for (…)
…
if done
…
break
end if
if goToNext
…
next
end if
if restartBlock
…
redo
end if
…
}
Now, let’s say the “if done” block gets so big that you want to extract a method out of it:
myIfDoneMethod
…
break
end method
Oops, this won’t work… “break” makes no sense in a method without a loop. So now you’ve gotta change the loop and the method to have a condition that simulates “break”. If you wrote your loop with the condition in the first place, you wouldn’t have to deal with this.
February 8th, 2009 at 4:12 am
[...] Ruby’s Most Underused Keyword [...]
February 8th, 2009 at 3:37 pm
Here’s how I once used redo. To generate a feed of user’s activity, I wanted to have an item like “posted 3 videos: x y z”. In order to achieve that, I’d loop over a mixed up array of all kinds of activities… Once I begin catching videos, I’d detect hitting a non-video item, render the partial for them and redo.
February 9th, 2009 at 4:35 am
Some quick Ruby tips…
February 16th, 2009 at 7:57 am
[...] the original here: Ruby, Rails, Web2.0 » Blog Archive » Ruby’s Most Underused Keyword argus-panoptes, attending, block, code, february-6th, magnus, news, peter, peter-cooper, redo, [...]
March 10th, 2010 at 5:01 pm
[...] Ruby, Rails, Web2.0 » Blog Archive » Ruby’s Most Underused Keyword – [...]