snippetrubyMinor
Convert to Roman numerals in Ruby
Viewed 0 times
convertromannumeralsruby
Problem
Challenged by Sandy Metz's newsletter, I've tried to implement this kata from exercism. However, my solution, which passes the tests, is quite a lot simpler than Sandy's solution. Ok, so she's using refinements and also supports the inverse, but still... As she's probably smarter than me, do I miss something?
class Fixnum
ROMAN = {
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"
}
def to_roman
result = ""
ROMAN.reduce(self) { |number, (divider, letter)|
letter_multiplier, remainder = number.divmod(divider)
result << (letter * letter_multiplier)
remainder
}
result
end
endSolution
I like your solution. You usually see it done with
I'd write:
Note that this is a state machine: it iterates over an input with an initial state, on each loop the state is updated, and the result is the final state.
while/for loops that substract multipliers instead of taking the quotient/remainder. Some notes about your code:ROMAN.reduce(self) { |number, (divider, letter)|. In a multiline block is idiomatic to usedo/end.
- Prefer building an array +
joinat the end, that appending to a string. Not that it matters with those sizes, but in general it's the recommended way (+=in a string is typically O(n^2),
- inject` is a functional abstraction. While you can perform in-place updates within the block, it will break the assumption of many readers. One solution is to encapsulate the output within the state (using a hash, for example).
I'd write:
class Fixnum
ROMAN = {...}
def to_roman
ROMAN.reduce(number: self, result: []) do |state, (divider, letter)|
letter_multiplier, remainder = state[:number].divmod(divider)
new_result = state[:result] + [letter * letter_multiplier]
{number: remainder, result: new_result}
end[:result].join
end
endNote that this is a state machine: it iterates over an input with an initial state, on each loop the state is updated, and the result is the final state.
Code Snippets
class Fixnum
ROMAN = {...}
def to_roman
ROMAN.reduce(number: self, result: []) do |state, (divider, letter)|
letter_multiplier, remainder = state[:number].divmod(divider)
new_result = state[:result] + [letter * letter_multiplier]
{number: remainder, result: new_result}
end[:result].join
end
endContext
StackExchange Code Review Q#129896, answer score: 3
Revisions (0)
No revisions yet.