For a Project Euler problem, I made a Ruby roman numeral converter:
def romanize(num)
digits = {
1000 => "M",
900 => "CM", 500 => "D", 400 => "CD", 100 => "C",
90 => "XC", 50 => "L", 40 => "XL", 10 => "X",
9 => "IX", 5 => "V", 4 => "IV", 1 => "I"
}
digits.reduce("") do |acc, digit|
key, numeral = digit
occurances, num = num.divmod(key)
acc + (numeral * occurances)
end
end
This works great, but I'm not sure it's good Ruby. Specifically, I'm wondering if the body of the reduce
block could be shortened, and if it could made more clear.
For example, I don't think its that obvious that num
is being modified in the middle of the reduce
block.
Also, I know I can monkey patch this into the Fixnum
class, but that's not what I'm looking for.
Update #1
@Tokland's answer taught me some new things. His answer works well, and is pretty clever.
If I take just two of his concepts and apply them to mine, the digits.reduce("") ...
block can be simplified to:
digits.map do |key, numeral|
occurances, num = num.divmod(key)
numeral * occurances
end.join
This needs one less line because it deconstructs the digit key/value in the block arguments. Also, it creates an array which is joined into a string at the end. For big numbers (around 100-trillion), this is much quicker.
The only problem I still see is that it's modifying the num
variable, which is essentially global state to the block.
|(key, numeral)|
->|key, numeral|
\$\endgroup\$