Конвертер римских цифр в целые числа с Python, используя словарь - PullRequest
0 голосов
/ 28 сентября 2018

Я учусь кодировать, и у меня было упражнение, чтобы сделать преобразование римских цифр в целые числа.Я понимаю, что есть много разных способов написания этой программы, но я был бы признателен, если бы вы помогли мне найти мою ошибку.Я хотел бы услышать предложения о том, как написать это лучше, но то, что я действительно мог бы использовать прямо сейчас, это понимание того, что я сделал неправильно с этим.

У меня есть словарь под названием 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».или "СМ" и "М".Я знаю, почему это происходит, потому что я попросил итерацию.Но можно ли попросить мою программу прекратить итерации, когда она возвращает значение?Я чувствую, что я действительно близок к решению, но я просто запутался в этом моменте.

Спасибо!

Ответы [ 5 ]

0 голосов
/ 28 сентября 2018

Возможно, было бы лучше представить значения в виде списка кортежей, поскольку это позволяет нам определять в них порядок , и поэтому мы можем избежать совпадения 'I' в случае, если строка содержит при этомточка '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 домы находим совпадение.Но как только мы обрабатываем «пятидесятые» (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
0 голосов
/ 28 сентября 2018

Нет необходимости перебирать словарь.Мой код немного отличается, но я постарался сохранить как можно больше вашего кода.

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)
0 голосов
/ 28 сентября 2018

Еще один способ посмотреть на это, я думаю

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)    
0 голосов
/ 28 сентября 2018

Подсчитайте количество вхождений каждой цифры в строке вместо проверки строки на наличие вхождений, затем просто удалите вхождения, где появляются двойные буквы, такие как CM, поэтому удалите nulber вхождений значения 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")
0 голосов
/ 28 сентября 2018

Вы должны убедиться, что вы потребляете все символы, которые вносят вклад в сумму.Поскольку все «атомарные» литералы, состоящие из нескольких символов, начинаются с единицы с более низким значением, а в противном случае на первом месте стоят единицы с более высоким значением, возможен простой жадный подход:

  • Попробуйте преобразовать первые два символа какв целом, если это невозможно, конвертируйте первый одиночный символ.
  • Переместиться на соответствующее количество шагов вперед.

    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)
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...