从右到左的解决方案有点 Pythonic(没有索引)并且相对较短。
算法:
- 反转罗马数字并将其映射到数字列表
- 找出应该减去哪些数字,然后对列表求和
例子:
'xiv'
=> sum(5, -1, 10)
=>14
def parse_roman(s):
numerals = {'M':1000, 'D':500, 'C':100, 'L':50, 'X':10, 'V':5, 'I':1}
n = 0
last_value = 0
# e.g. convert 'xiv' to (5, 1, 10)
for value in (numerals[c] for c in reversed(s.upper())):
# debugging
v = (value, -value)[value < last_value]
print('{:6} += {:5} <== cur, prev = {}, {}'.format(n, v, value, last_value))
# subtract smaller values that come after larger ones, otherwise add
n += (value, -value)[value < last_value]
last_value = value
return n
输出:
parse_roman('MMCMXCVIII')
0 += 1 <== cur, prev = 1, 0
1 += 1 <== cur, prev = 1, 1
2 += 1 <== cur, prev = 1, 1
3 += 5 <== cur, prev = 5, 1
8 += 100 <== cur, prev = 100, 5
108 += -10 <== cur, prev = 10, 100
98 += 1000 <== cur, prev = 1000, 10
1098 += -100 <== cur, prev = 100, 1000
998 += 1000 <== cur, prev = 1000, 100
1998 += 1000 <== cur, prev = 1000, 1000
2998
注意:最好找到一种(短的,内联的)方法来动态更改序列的符号。例如,(5, 1, 10)
==> (5, -1, 10)
。
更新:这与我放弃之前的情况一样接近。它与上面的代码相同,但它使用itertools.tee()
withzip()
生成先前值和当前值对,以消除对状态变量的需要。单次调用next(cur)
使该列表比prev
我们需要确定是否添加或减去当前值的所有状态短一个。
例子:
cur, prev = (5, 1, 10), (5, 1, 10)
# Take one from cur and zip the rest
next(cur) + sum(... zip(cur, prev))
# 5 + ... zip( (1, 10), (5, 1, 10) ) ==> 5 + ... ((1, 5), (10, 1))
代码:
from itertools import tee
def parse_roman(s):
numerals = {'M':1000, 'D':500, 'C':100, 'L':50, 'X':10, 'V':5, 'I':1}
cur, prev = tee(numerals[c] for c in reversed(s.upper()))
return next(cur) + sum((cur, -cur)[cur < prev] for cur, prev in zip(cur,prev))