【问题标题】:Check if an input is a valid roman numeral检查输入是否是有效的罗马数字
【发布时间】:2014-01-07 14:00:45
【问题描述】:

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

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 的数字,因此它们必须存在。

【问题讨论】:

标签: python python-3.x


【解决方案1】:

编写一个从整数到罗马的转换器是一个标准的面试问题。我曾经写过以下双向实现(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

【讨论】:

    【解决方案2】:
    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
    

    返回一个布尔值是否给定 'numeral' 是否为罗马数字。

    【讨论】:

      【解决方案3】:

      除了已经指出的设计问题,我只想回答这个问题为什么你的for循环没有遍历所有的数字

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

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

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

      【讨论】:

        【解决方案4】:

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

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

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

        【讨论】:

        • 这只是return not set(numeral.upper()).difference('MDCLXVI()') 虽然它避免了设置的comps
        • 只是混淆了为什么两个组合组合(只需set(numeral.upper()) 第一个就足够了),而第二个甚至不需要从一个集合开始......
        • @JonClements 我只是喜欢集合推导。不,说真的,你完全正确。
        【解决方案5】:

        我构建了一种解决方案,您可以根据自己的需要轻松修改它,甚至可以获取号码。如果来自用户的字符串是有效数字,我的程序正在打印,但您可以轻松更改它以获取实际值。

        def main():
            # YOUR CODE GOES HERE
            # Please take input and print output to standard input/output (stdin/stdout)
            # E.g. 'input()/raw_input()' for input & 'print' for output
            N = 1
            for i in range(N):
                romanStr = input() # your str
                units = ["I", "II", "III" , 'IV' , 'V', 'VI', 'VII', 'VIII', "IX"]
                tens = ['X', "XX", "XXX", 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC']
                hundreds = ['C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM']
                thousands = ['M', 'MM', 'MMM']
                allTypes = [units, tens, hundreds, thousands][::-1]
                isError = False
                lenRom = len(romanStr)
                copy = romanStr
                while len(copy) > 0 and len(allTypes) > 0:
                    currentUnit = allTypes[0]
                    # check units
                    firstNum = copy[0]
                    lastTwo = copy[:2]
                    last3 = copy[:3]
                    last4 = copy[:4]
                    isLastTwo = lastTwo in currentUnit and len(lastTwo) == 2
                    isLast3 = last3 in currentUnit and len(last3) == 3
                    isLast4 = last4 in currentUnit and len(last4) == 4
                    
                    if (firstNum in currentUnit and not (isLastTwo or isLast3 or isLast4) ):
                        copy = copy[1::]
                        isError = False
                    elif (isLastTwo and not (isLast3 or isLast4) ):
                        copy = copy[2::]
                        isError = False
                    elif (isLast3 and not (isLast4) ):
                        copy = copy[3::]
                        isError = False
                    elif (isLast4):
                        copy = copy[4::]
                        isError = False
                    else:
                        
                        isError = True
                        # copy = ''
                    allTypes.pop(0)
                
                if (isError or len(copy) != 0 ):
                    print("NO")
                    return 0
                else:
                    print("YES")
                    return 1
        
            return 0
        
        if __name__ == '__main__':
            main()
        

        【讨论】:

          【解决方案6】:

          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)
          

          【讨论】:

          • 这不完整。例如,“IIX”不是有效的罗马数字。
          猜你喜欢
          • 2019-04-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-04-22
          • 1970-01-01
          • 2013-12-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多