关于python:检查输入是否是有效的罗马数字

Check if an input is a valid roman numeral

我有一个程序可以把罗马数字转换成整数,反之亦然。我的问题是我不知道如何创建一个函数来检查用户输入是否是有效的罗马数字。我的代码:

1
2
3
4
5
6
7
8
9
10
11
def checkIfRomanNumeral(numeral):
"""Controls that the userinput only contains valid roman numerals"""
    numeral = numeral.upper()
    validRomanNumerals = ["M","D","C","L","X","V","I","(",")"]
    for letters in numeral:
        if letters not in validRomanNumerals:
            print("Sorry that is not a valid roman numeral")
            return True
        elif letters in validRomanNumerals:
            romanToInt(numeral)
            break

我认为现在的问题是函数只检查输入(数字)中的第一个字母,因为for循环。如果输入的任何字母不是罗马数字,有人能帮我检查整个输入和打印功能吗("对不起,这不是一个有效的罗马数字")。列表中的括号validromanumerals用于转换大于4000的数字,因此它们必须存在。


写一个从英特到罗马人的转换是一个标准的面试问题。我曾经编写过以下双向实现(toString--十进制到罗马;parse--罗马到十进制)。这一实施过程满足了罗马数字表示的一些附加标准,这些标准不是强制性的,但通常遵循:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
'''
Created on Feb 7, 2013

@author: olegs
'''


ROMAN_CONSTANTS = (
            ("","I","II","III","IV","V","VI","VII","VIII","IX" ),
            ("","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" ),
            ("","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" ),
            ("","M","MM","MMM","",  "", "-", "",   "",    ""   ),
        )

ROMAN_SYMBOL_MAP = dict(I=1, V=5, X=10, L=50, C=100, D=500, M=1000)

CUTOFF = 4000
BIG_DEC = 2900
BIG_ROMAN ="MMCM"
ROMAN_NOUGHT ="nulla"

def digits(num):
    if num < 0:
        raise Exception('range error: negative numbers not supported')
    if num % 1 != 0.0:
        raise Exception('floating point numbers not supported')
    res = []
    while num > 0:
        res.append(num % 10)
        num //= 10
    return res

def toString(num, emptyZero=False):
    if num < CUTOFF:
        digitlist = digits(num)
        if digitlist:
            res = reversed([ ROMAN_CONSTANTS[order][digit] for order, digit in enumerate(digitlist) ])
            return"".join(res)
        else:
            return"" if emptyZero else ROMAN_NOUGHT
    else:
        if num % 1 != 0.0:
            raise Exception('floating point numbers not supported')
        # For numbers over or equal the CUTOFF, the remainder of division by 2900
        # is represented as above, prepended with the multiples of MMCM (2900 in Roman),
        # which guarantees no more than 3 repetitive Ms.
        return BIG_ROMAN * (num // BIG_DEC) + toString(num % BIG_DEC, emptyZero=True)

def parse(numeral):
    numeral = numeral.upper()
    result = 0
    if numeral == ROMAN_NOUGHT.upper():
        return result
    lastVal = 0
    lastCount = 0
    subtraction = False
    for symbol in numeral[::-1]:
        value = ROMAN_SYMBOL_MAP.get(symbol)
        if not value:
            raise Exception('incorrect symbol')
        if lastVal == 0:
            lastCount = 1
            lastVal = value
        elif lastVal == value:
            lastCount += 1
            # exceptions
        else:
            result += (-1 if subtraction else 1) * lastVal * lastCount
            subtraction = lastVal > value
            lastCount = 1
            lastVal = value
    return result + (-1 if subtraction else 1) * lastVal * lastCount


您可以将输入和有效的文本转换为集合,然后对它们进行减法运算,而不是循环:

1
2
3
4
def checkIfRomanNumeral(numeral):
    numeral = {c for c in numeral.upper()}
    validRomanNumerals = {c for c in"MDCLXVI()"}
    return not numeral - validRomanNumerals

如果numeral有效,返回True,否则返回False。(假设空字符串有效)


除了已经指出的设计问题外,我想回答一个问题,为什么你的for循环没有通过所有的数字

如果您的代码认为这些条目有效,那么循环将进入elif子句。它先叫romanToInt(numeral),然后叫break。这是你的问题:break把它拿出来。

图:只要满足本例中的条件,循环将停止通过i in list

1
2
3
4
for i in list:
   # do something
   if condition:
       break #"Stop the innermost loop now!"


1
2
3
4
5
6
7
8
9
10
11
def checkIfRomanNumeral(numeral):
"""Controls that the userinput only contains valid roman numerals"""
    numeral = numeral.upper()
    validRomanNumerals = ["M","D","C","L","X","V","I","(",")"]
    valid = True
    for letters in numeral:
        if letters not in validRomanNumerals:
            print("Sorry that is not a valid roman numeral")
            valid = False
            break
    return valid

返回一个布尔值"数字"是否为罗马数字。


for循环后调用romantoint

1
2
3
4
5
6
7
8
9
 def checkIfRomanNumeral(numeral):
    """Controls that the userinput only contains valid roman numerals"""
     numeral = numeral.upper()
     validRomanNumerals = ["M","D","C","L","X","V","I"]
     for letters in numeral:
        if letters not in validRomanNumerals:
            print("Sorry that is not a valid roman numeral")
            return False
     romanToInt(numeral)