【问题标题】:Roman Numerals to Integers Converter with Python using a dictionary使用字典的 Python 罗马数字到整数转换器
【发布时间】:2019-03-04 09:56:28
【问题描述】:

我正在学习编码,并且我有一个练习来将罗马数字转换为整数。我意识到编写这个程序有很多不同的方法,但如果你能帮助我找出我的错误,我将不胜感激。我很想听听有关如何更好地编写它的建议,但我现在真正可以使用的是了解我在这方面做错了什么。

我有一本名为roman_numerals 的字典。它的keys 是罗马数字值,它的values 是它们的匹配整数。

roman_numerals = {"M":1000,"CM":900,"D":500,"CD":400,"C":100,"XC":90,"L":50,"XL":40,"X":10,"V":5,"IV":4,"I":1}

def roman_int(user_choice):
    if user_choice == "1":
        user_roman = input("What numeral would you like to convert?\n").upper()
        resultI = 0
        for k,v in roman_numerals.items():          
            if user_roman == k:
                resultI += roman_numerals.get(user_roman)
            else:
                for i in user_roman:
                    if i in roman_numerals.keys():
                        if i == k:
                            resultI += v
    print(resultI)

当我运行我的代码时,如果我使用一个等于多个字符的key 的数字(例如“IV”),我得到的结果是“IV”和“V”的加法”。或“CM”和“M”。 我知道它为什么会发生,因为我要求进行迭代。但是,我可以要求我的程序在返回值时停止迭代吗?我觉得我真的很接近解决方案,但我现在只是感到困惑。

谢谢!

【问题讨论】:

  • 我相信我的回答应该可以解决您的问题

标签: python python-3.x dictionary for-loop


【解决方案1】:

您必须确保消耗所有对总和有贡献的字符。由于所有的多字符“原子”字面量都以较低值的单位开头,否则,较高值的单位首先出现,所以一种简单的贪婪方法是可行的:

  • 尝试将前两个字符作为一个整体进行转换,如果不可能,请转换第一个单个字符。
  • 向前移动适当的步数。

    def roman_int(user_roman):
        user_roman = user_roman.upper()
        resultI = 0
    
        while user_roman:
            # try first two chars
            if user_roman[:2] in roman_numerals:
                resultI += roman_numerals[user_roman[:2]]
                # cut off first two chars
                user_roman = user_roman[2:]
            # try first char
            elif user_roman[:1] in roman_numerals:
                resultI += roman_numerals[user_roman[:1]]
                # cut off first char
                user_roman = user_roman[1:]
            else:
                print('No roman number')
                return
        print(resultI)
    

【讨论】:

    【解决方案2】:

    将值表示为元组列表可能会更好,因为这允许我们在其中定义 order,因此我们可以避免匹配 'I',以防字符串包含 at那一点'IX'。所以我们可以将转换定义为:

    roman_numerals = [
        ('M', 1000),
        ('CM', 900),
        ('D', 500),
        ('CD', 400),
        ('C', 100),
        ('XC', 90),
        ('L', 50),
        ('XL', 40),
        ('X', 10),
        ('IX', 9),
        ('V', 5),
        ('IV', 4),
        ('I', 1)
    ]
    

    请注意,您忘记使用IX,这在一定程度上会造成问题,因为它将IX 解释为11

    现在我们可以通过每次执行str.startswith() [Python-doc]检查来处理字符串,从我们找到前缀的那一刻起,加上对应的值,并提前字符串的偏移量,比如:

    def roman_int(user_choice):
        ix = 0
        result = 0
        while ix < len(user_choice):
            for k, v in roman_numerals:
                if user_choice.startswith(k, ix):
                    result += v
                    ix += len(k)
                    break
            else:
                raise ValueError('Invalid Roman number.')
        return result
    

    因此,我们通过字符串枚举,并且每次寻找匹配一个罗马数字。例如:

    >>> roman_int('MCMXC')
    1990
    >>> roman_int('MCMXCIII')
    1993
    >>> roman_int('MMXVIII')
    2018
    

    如果我们输入无效字符,系统也会出错:

    >>> roman_int('MMXQVIII')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 11, in roman_int
    ValueError: Invalid Roman number.
    

    然而,上面的方法不是很有效:每次我们枚举roman_literals,直到找到匹配。但是一旦我们处理了“50 年代”(L),那么我们就知道我们永远不会再解析“千”(M)了。我们可以通过记住roman_numerals 中的索引来提高性能:

    def roman_int(user_choice):
        ix = 0
        iy = 0
        result = 0
        while ix < len(user_choice):
            while iy < len(roman_numerals) and not user_choice.startswith(roman_numerals[iy][0], ix):
                iy += 1
            if iy < len(roman_numerals):
                result += roman_numerals[iy][1]
                ix += len(roman_numerals[iy][0])
            else:
                raise ValueError('Invalid Roman numeral')
        return result
    

    这又产生了我们预期的结果:

    >>> roman_int('MDCCLXXVI')
    1776
    >>> roman_int('MCMLIV')
    1954
    >>> roman_int('MCMXC')
    1990
    >>> roman_int('MMXIV')
    2014
    >>> roman_int('MMXVIII')
    2018 
    

    但也更严格,例如CMM 不是有效的罗马数字,而MCM 是:

    >>> roman_int('CMM')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 12, in roman_int
    ValueError: Invalid Roman numeral
    >>> roman_int('MCM')
    1900
    

    【讨论】:

      【解决方案3】:

      计算字符串中每个数字的出现次数,而不是检查字符串是否出现,然后只需删除出现双字母(例如CM)的出现次数,因此删除出现CM的次数*C的值和M

      roman_numerals = {"M":1000,"CM":900,"D":500,"CD":400,"C":100,"XC":90,"L":50,"XL":40,"X":10,"IX":9, "V":5,"IV":4,"I":1}
      
      def roman_int(user_choice):
          if user_choice == "1":
              result = 0
              user_roman = input("What numeral would you like to convert?\n").upper()
              for k,v in roman_numerals.items():          
                  result += v * user_roman.count(k)
                  if len(k) == 2:
                      result -= roman_numerals[k[0]] * user_roman.count(k)
                      result -= roman_numerals[k[1]] * user_roman.count(k)                
          print(result)
      
      roman_int("1")
      

      【讨论】:

        【解决方案4】:

        无需遍历字典。我的代码略有不同,但我已尝试尽可能多地保留您的代码。

        roman_numerals =  {"M":1000,"CM":900,"D":500,"CD":400,"C":100,"XC":90,"L":50,"XL":40,"X":10,"V":5,"IV":4,"I":1}
        
        def roman_int(user_choice):
            if user_choice == "1":
                user_roman = input("What numeral would you like to convert?\n").upper()
                result = 0
                values = []
        
                # return the result if the input is in the dictionary
                try:
                    result = roman_numerals[user_roman]
                except KeyError:
                    # split up the user input and convert each character into corresponding numeral
                    for i in user_roman:
                        try:
                            value = roman_numerals[i]
                            values.append(value)
                        # if user enters character not used in roman numerals
                        except KeyError:
                            print("Not valid input")
                    # loop through all values and add them up
                    for i, value in enumerate(values):
                        try:
                            # if a value is larger than the next value, add it
                            if value > values[i+1]:
                                result += value
                            # else the number is obtained by substracting the smaller value from the larger value
                            else:
                                actual_value = values[i+1] - value
                                result = result + actual_value
                                #set the next number to 0 as it has already been added
                                values[i+1] = 0
                        except IndexError:
                            # this try except block catches the IndexError exception caused when i+1 > len(values)
                            pass
                print(result)
        

        【讨论】:

          【解决方案5】:

          我猜是另一种看待它的方式

          roman_numerals = {"M":1000,"CM":900,"D":500,"CD":400,"C":100,"XC":90,"L":50,"XL":40,"X":10,"V":5,"IV":4,"I":1}
          
          def roman_int(user_choice):
              if user_choice == "1":
                  user_roman = input("What numeral would you like to convert?\n").upper()
                  resultI = 0
                  pos = 0
                  try:
                      resultI = roman_numerals[user_roman]
                  except:
                      try:
                          while pos < len(user_roman):
                              resultI += roman_numerals[user_roman[pos]]
                              pos+=1
                      except:
                          print('Not present in our dictionary')
              print(resultI)    
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-01-17
            • 2019-01-09
            • 2011-10-25
            • 1970-01-01
            • 2023-01-10
            相关资源
            最近更新 更多