As an exercise in coding, I recently rewrote an old ruby program into a new one. Functionally the code for the new program does the exact same thing as the old one – both programs run simulations for the Monty Hall Problem. But the new one was written with the intention of being easier to read and DRY’er.
The original program was from when I was first learning ruby, and was definitely painful to read at times. Going straight down the program, here’s a self-critique of some of the code.
Complaint #1: lack of inline boolean expressions
This is supposed to read a command line argument, and set it to a boolean true
if the argument is the string “true”:
1
|
|
The danger in this is that show_details_options
would not be set at all if the if
condition isn’t met. It doesn’t in this case since it was already initialized on the line before, but there’s no use in taking this risk if it’s perfectly avoidable.
My new version ditches the if
statement altogether:
1
|
|
Complaint #2: using and
and or
instead of &&
and ||
and
and or
are meant for flow control, not to be used as boolean operators.
What can I say? I was young, I was naive, I hadn’t read this blog post by Avdi Grimm.
Complaint #3: not using built in array methods
Example 1
Take this snippet:
1 2 3 4 5 6 |
|
The purpose of this was to build a new array from 1 to total_number_of_doors
while excluding specific values, namely contestants_guess
and possible_open_doors
.
My approach was to build a brand new array, while checking to make sure the new values being appended to the new array were neither contestants_guess
nor number_of_door_with_car
. That works, but the same effect can be achieved much more concisely with some built in array functions in ruby. The refactored result is much shorter:
1
|
|
Note that you can convert a range into an array in ruby (eg – (1..4).to_a
becomes [1, 2, 3, 4]
). The array difference operator (aka array subtraction) allows us to easily exclude particular values. If I wanted to exclude 1 and 4 from the array of 4 I can say [1, 2, 3, 4] - [1, 4]
– which seems trivial with hardcoded values but becomes handy with variable values which need to be excluded.
Example 2
Here I’m selecting a random value from an array:
1
|
|
When Array’s “sample” method already does precisely the same thing:
1
|
|
Complaint #4: methods that took way too many lines of code
My original method used to run a simulation was 86 lines of code long. Just a few more than Sandi Metz’s five lines per method rule. The body of the revised version is a little closer at 9 lines (excluding whitespace):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Not only is this less lines of code, but also reads a lot better… going down the line of methods being called you see “set_up_doors”, “contestants_first_guess”, “reveal_door”, etc. and can see exactly how the flow of the simulation works.
Conclusion
I was hesitant to even look at my original program because I knew that there would be lots of cringe-worthy moments to be had. But it’s satisfying to be able to quickly identify elegant solutions to problems that used to give me a hassle – I’d recommend it as an exercise to anyone.