Python 检查输入是否为有效的罗马数字

Python 检查输入是否为有效的罗马数字,python,python-3.x,Python,Python 3.x,我有一个程序可以把罗马数字转换成整数,反之亦然。我的问题是,我真的不知道如何创建一个函数来检查用户输入是否是有效的罗马数字。我现在的代码: def checkIfRomanNumeral(numeral): """Controls that the userinput only contains valid roman numerals""" numeral = numeral.upper() validRomanNumerals = ["M", "D", "C", "L", "

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

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循环,函数只检查输入中的第一个字母(数字)。如果输入的任何字母不是罗马数字,请有人帮我检查整个输入并打印(“对不起,这不是有效的罗马数字”)。validRomanNumerals列表中的括号用于转换大于4000的数字,因此它们必须在那里。

for
循环后调用
romantoint

 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)

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

'''
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
“”
创建于2013年2月7日
@作者:奥列格斯
'''
罗马常数=(
(“一”、“二”、“三”、“四”、“五”、“六”、“七”、“八”、“九”),
(“X”、“X”、“XX”、“XXX”、“XL”、“L”、“LX”、“LXX”、“XC”),
(“,”C“,”CC“,”CCC“,”CD“,”D“,”DC“,”DCC“,”DCCC“,”CM“),
(“,”M“,”MM“,”MMM“,”MMM“,”MMM“,”MMM“,”MMM“,”MMM“,”MMM“,”MMM“,”MMM“,”MMM“,”MMM“,”MMM“,”MMM“,”MMM,
)
罗马符号(I=1,V=5,X=10,L=50,C=100,D=500,M=1000)
截止值=4000
大12月=2900
BIG_ROMAN=“MMCM”
ROMAN_nough=“nulla”
def数字(num):
如果num<0:
引发异常('范围错误:不支持负数')
如果数量为%1!=0.0:
引发异常('不支持浮点数')
res=[]
当num>0时:
res.append(数量%10)
num/=10
返回res
def toString(num,emptyZero=False):
如果num<截止值:
数字列表=数字(num)
如果是数字列表:
res=反向([ROMAN_常量[order][digit]表示顺序,枚举中的数字(数字列表)])
返回“.join(res)
其他:
如果为空,则返回“”
其他:
如果数量为%1!=0.0:
引发异常('不支持浮点数')
#对于大于或等于截止值的数字,其余数除以2900
#如上所示,以MMCM的倍数(罗马字母2900)作为前缀,
#保证不超过3毫秒的重复。
返回BIG\u-ROMAN*(num//BIG\u-DEC)+toString(num%BIG\u-DEC,emptyZero=True)
def解析(数字):
数字=数字。上()
结果=0
如果数字==罗马字母大写()
返回结果
lastVal=0
lastCount=0
减法=假
对于数字[:-1]中的符号:
value=ROMAN\u SYMBOL\u MAP.get(SYMBOL)
如果不是值:
引发异常(“不正确的符号”)
如果lastVal==0:
lastCount=1
lastVal=值
elif lastVal==值:
lastCount+=1
#例外情况
其他:
结果+=(-1,如果减法,则为1)*lastVal*lastCount
减法=最后值>值
lastCount=1
lastVal=值
返回结果+(-1,如果减法,则为1)*lastVal*lastCount
返回一个布尔值,表示给定的
“数字”是否为罗马数字。

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

如果代码认为条目有效,则循环进入
elif
子句 它调用
romanToInt(numeric)
,然后调用
break
。这就是你的问题:
break
把它拿出来

插图:一旦满足本例中的条件,循环将停止通过列表中的
i

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

您可以将输入文字和有效文字转换为集合,然后减去它们,而不是循环:

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

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

当无效时不应该返回
False
?如何定义有效的罗马数字?罗马数字必须从左向右递减。除了IV、IX等,这是规则的例外。密切相关:您可以尝试一个正则表达式:这只是
返回未设置(numeric.upper())。差异('MDCLXVI()')
,但这避免了设置comps混淆为什么两个设置comps(只是
设置(numeric.upper())
对于第一个就足够了)第二个甚至不需要是一个集合就可以开始…@JonClements我只是喜欢集合理解。不,说真的,你完全正确。这还不完整。例如,“IIX”不是有效的罗马数字。
def checkIfRomanNumeral(numeral):
    numeral = {c for c in numeral.upper()}
    validRomanNumerals = {c for c in "MDCLXVI()"}
    return not numeral - validRomanNumerals