Простой конвертер кода Морзе (Python)

Я создал простой преобразователь текста в код Морзе на Python, и мне было интересно, есть ли более простой / короткий способ сделать это. Есть ли способ сгенерировать словарь без необходимости жестко кодировать каждую опцию?

def main():

    print("This program translates words to morse coden")

    words = input("Enter a word or phrase to be encoded: ")
    words = words.upper()

    codeDict = createDict()

    i = 0

    try:
        while (i < len(words)):
            if (words[i] != " "):
                print(codeDict[words[i]], end = " ")
            else:
                print("/", end = " ")
            i += 1
    except:
        print("nInvalid characters found - refer to morse code alphabet")


def createDict():
    code = {'A': '.-',
            'B': '-...',
            'C': '-.-.',
            'D': '-..',
            'E': '.',
            'F': '..-.',
            'G': '--.',
            'H': '....',
            'I': '..',
            'J': '.---',
            'K': '-.-',
            'L': '.-..',
            'M': '--',
            'N': '-.',
            'O': '---',
            'P': '.--.',
            'Q': '--.-',
            'R': '.-.',
            'S': '...',
            'T': '-',
            'U': '..-',
            'V': '...-',
            'W': '.--',
            'X': '-..-',
            'Y': '-.--',
            'Z': '--..',
            '1': '.----',
            '2': '..---',
            '3': '...--',
            '4': '....-',
            '5': '.....',
            '6': '-....',
            '7': '--...',
            '8': '---..',
            '9': '----.',
            '0': '-----'}

    return code


main()

3 ответа
3

ПРИМЕЧАНИЕ. Вы не указываете версию Python, поэтому я предполагаю, что вы используете последнюю версию Python — я лично использую Python 3.9, поэтому я пишу с учетом этого в качестве предположения. Я написал этот ответ для совместимости с Python 3.6 и новее, есть место, где я даю обходной путь для версий Python 3 до 3.6.

Во-первых, я не уверен, что ты сможешь генерировать эквиваленты азбуки Морзе буквы в любом шаблоне — насколько мне известно, для этого не существует алгоритма. Так что вам все равно придется статически кодировать в списке. Однако у вас есть некоторые ненужные моменты, связанные с этим.

Ненужная функция для создания статического словаря

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

Просто определите его в начале вашей программы один раз, не регенерируйте его каждый раз. И затем обратитесь к этой переменной соответственно (я назвал ее LETTERS_TO_MC, чтобы было более понятно, для чего она нужна). Но также добавьте регистр перевода для места — это сократит ваши логические сравнения позже.

LETTERS_TO_MC = {
    'A': '.-',
    'B': '-...',
    'C': '-.-.',
    'D': '-..',
    'E': '.',
    'F': '..-.',
    'G': '--.',
    'H': '....',
    'I': '..',
    'J': '.---',
    'K': '-.-',
    'L': '.-..',
    'M': '--',
    'N': '-.',
    'O': '---',
    'P': '.--.',
    'Q': '--.-',
    'R': '.-.',
    'S': '...',
    'T': '-',
    'U': '..-',
    'V': '...-',
    'W': '.--',
    'X': '-..-',
    'Y': '-.--',
    'Z': '--..',
    '1': '.----',
    '2': '..---',
    '3': '...--',
    '4': '....-',
    '5': '.....',
    '6': '-....',
    '7': '--...',
    '8': '---..',
    '9': '----.',
    '0': '-----',
    ' ': "https://codereview.stackexchange.com/"
}

Переименовать main() к translate()

Хотя технически это необязательный случай, обычно функции должны указывать на то, что они на самом деле делать — в данном случае они что-то переводят, так что позвольте позвонить main() по названию того, что он делает — translate().

Уменьшать translate() чтобы делать только то, что он говорит — перевод — и сделать функцию более функциональной

Давайте уменьшим translate функция специально для перевода и последующего возврата строки кода Морзе. Мы позволим получению информации от конечного пользователя стать частью следующего предложения по улучшениям, которое я делаю.

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

Во-вторых, из-за того, что мы ранее вносили в словарь пробелы, нам больше не нужен неуклюжий if заявления, ни print() утверждения с конкретными конечными аргументами.

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

def translate(words):
    code=""

    for char in words.upper():
        code += f' {LETTERS_TO_MC[char]} '

    return code

Вы заметите, что я использую то, что мы называем ф-струна в translate функция. ** Это доступно только в Python 3.6 и новее. Если вы используете старую версию Python до Python 3, вам придется использовать строку формата старого стиля, т.е. code += ' {} '.format(LETTERS_TO_MC[char]) вместо строки выше.

Это делает функцию меньше и менее сложной!

Обратите внимание, что в этой функции нет обработки ошибок — если символ не может быть преобразован, Python вызовет KeyError потому что буквы не существует в словаре. Это будет показано в следующем разделе, где мы сделаем этот скрипт исполняемым!

Использовать if __name__ == "__main__": для прямой обработки исполнения скрипта

Предположим, что в будущем вы, возможно, захотите иметь возможность импортировать translate в другом месте как часть сценария. Вот почему мы написали это так же, как и в последнем предложении. Однако вы по-прежнему хотите выполнить этот сценарий напрямую и получить ввод. Итак, давайте сделаем это!

Вместо того, чтобы просто звонить translate() в конце .py файл, давайте поместим его под if __name__ == "__main__": блок в конце. Кроме того, поскольку мы уменьшили translate функция ТОЛЬКО для перевода, здесь мы можем выполнить биты пользовательского ввода.

if __name__ == "__main__":
    print("This program translates words into to morse code.n")

    words = input("Enter a word or phrase to be encoded: ")

    try:
        print(translate(words))
    except KeyError:
        print("The word or phrase you've provided contains invalid characters - we can only convert the letters A-Z, numbers, and spaces to Morse Code in this program.")

Здесь мы обрабатываем прямое выполнение сценария вашего кода Python (как при вызове python3 morsecode-convert.py или что-то подобное в командной строке). Мы используем обработку try / except, как и раньше (за исключением того, что мы ЗНАЕМ, что недопустимый символ вызывает ошибку KeyError в translate, поэтому мы перехватываем только эти исключения и выводим пользователю приятное сообщение об ошибке), но мы делаем это только тогда, когда программа выполняется напрямую как сценарий. Это также предотвращает выполнение translate без фактических полезных данных, передаваемых в функцию, если вы когда-либо импортируете translate или ваш LETTERS_TO_MC dict в другую программу / сценарий / проект Python позже.

Вся переписанная программа с учетом приведенных выше предложений

Обратите внимание, что я помещаю несколько определений кода Морзе на строку в dict (по три на строку), чтобы уменьшить его, чтобы было удобнее читать при прокрутке; вы можете легко расширить его, как я делал ранее в своем ответе, но вы все равно можете легко определить, что в слове, даже таким образом.

LETTERS_TO_MC = {
    'A': '.-', 'B': '-...', 'C': '-.-.',
    'D': '-..', 'E': '.', 'F': '..-.',
    'G': '--.', 'H': '....', 'I': '..',
    'J': '.---', 'K': '-.-', 'L': '.-..',
    'M': '--', 'N': '-.', 'O': '---',
    'P': '.--.', 'Q': '--.-', 'R': '.-.',
    'S': '...', 'T': '-', 'U': '..-',
    'V': '...-', 'W': '.--', 'X': '-..-',
    'Y': '-.--', 'Z': '--..',  '1': '.----',
    '2': '..---', '3': '...--', '4': '....-',
    '5': '.....', '6': '-....', '7': '--...',
    '8': '---..', '9': '----.', '0': '-----',
    ' ': "https://codereview.stackexchange.com/"
}


def translate(words):
    code=""

    for char in words.upper():
        code += f' {LETTERS_TO_MC[char]} '

    return code
    

if __name__ == "__main__":
    print("This program translates words into to morse code.n")

    words = input("Enter a word or phrase to be encoded: ")

    try:
        print(translate(words))
    except KeyError:
        print("The word or phrase you've provided contains invalid characters - we can only convert the letters A-Z, numbers, and spaces to Morse Code in this program.")

  • Большое спасибо! Это было супер тщательно

    — am2021

  • 1

    Ха, кажется, я немного затянул с ответом. Единственное улучшение, которое я предлагаю, — это замена конкатенации строк внутри translate с участием return " ".join(LETTERS_TO_MC[char] for char in words.upper()). Это более питонический и более эффективный афайк. Он также избавится от начальных и конечных пробелов, которые, насколько я могу судить, не предназначены.

    — рискованный пингвин


  • 1

    @riskypenguin Хороший момент, я написал это, когда устал. Я могу вставить это.

    — Томас Уорд

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


Код Морзе диктат

См. Комментарии к вашему вопросу: Нам не нужна функция createDict, который следует называть create_dict (PEP 8) нам нужна только переменная:

CHAR_TO_MORSE = {'A': '.-',
                 ...
                 '0': '-----'}

Никогда не перебирать индексы в Python

В Python этот шаблон обычно не рекомендуется:

i = 0
while (i < len(words)):
    print(words[i])

Вам почти никогда не нужно перебирать элементы и обращаться к ним по их индексам. Вместо этого вы должны напрямую перебирать элементы итерации:

for word in words:
    print(word)

Если вам нужен индекс, вы можете использовать встроенный enumerate:

for index, word in enumerate(words):
    print(f"{index}: {word}")

words не является описательным именем переменной для строки. Если я перебираю переменную с именем words, Я ожидаю получить слова, а не отдельные символы. Я бы изменил это на text или же message а затем измените цикл на:

for char in text:
    ...

Это сразу дает понять, что происходит.


Обработка исключений

Не используйте голый, кроме. Это может затруднить прерывание программ и замаскировать другие проблемы, чем те, которые вы собираетесь выявить. Вместо этого вы должны только поймать KeyError, поскольку это единственная ошибка, которую вы обрабатываете явным образом.


слова[i] знак равно

Вы выполняете эту проверку на каждой итерации цикла, хотя на самом деле это не особый случай. Это в основном только переводит " " к "/", в остальном логика такая же. Таким образом, вы можете просто добавить его в свой char для отображения Морзе:

CHAR_TO_MORSE = {'A': '.-',
                 ...
                 '0': '-----',
                 ' ': "https://codereview.stackexchange.com/"}

Вызов main ()

В большинстве случаев вы должны заключить вызов в main() из сценария в if название == “основной«: условие.

if __name__ == "__main__":
    main()

Что делать, если название == “основной«: делать?


Полный код

Я также применил .upper() непосредственно к возвращаемому значению input(...) и переместил предложение Exception в цикл, чтобы пользователь мог лучше понять ошибку.

CHAR_TO_MORSE = {'A': '.-',
                 ...
                 '0': '-----',
                 ' ': "https://codereview.stackexchange.com/"}


def main():
    print("This program translates words to morse code", end="nn")

    text = input("Enter a word or phrase to be encoded: ").upper()

    for char in text:
        try:
            print(CHAR_TO_MORSE[char], end=" ")
        except KeyError:
            print(f"nInvalid character found: '{char}' - refer to morse code alphabet")
            break

join и составить список / выражения генератора

Если вы не настроены на печать кода Морзе до недопустимого символа, это идеальный вариант использования для join вместе с пониманием списка:

try:
    print(" ".join(CHAR_TO_MORSE[char] for char in text))
except KeyError:
    print(f"nInvalid character found - refer to morse code alphabet")

То, что мы здесь используем, на самом деле не понимание списка, а выражение генератора. Если вам интересно, вы можете узнать больше о Выражения генератора против представлений списков.

    Рассмотрение

    Thomas Ward и riskypenguin дали отличные комментарии в обзоре:

    • Удалите ненужные функции
    • Лучшие имена функций
    • Используйте главную охрану
    • Добавьте место в словарь, чтобы избежать особого случая
    • Перебирать индексы
    • Не используйте голый, кроме

    Я не буду их повторять. Вместо этого я должен упомянуть PEP-8: Руководство по стилю для кода Python имеет соглашения о кодировании, которым должен следовать каждый программист Python. Конкретно …

    Именование

    Имена переменных и функций должны быть в snake_case. Это означает createDict() должен быть назван create_dict(), и аналогично codeDict должно быть code_dict.

    Белое пространство

    Бинарные операторы обычно должны быть окружены одним пробелом, и вы точно следуете этому правилу.

    Однако есть исключение: = символ в параметрах ключевого слова технически не является бинарным оператором. Здесь в руководстве по стилю не рекомендуется использовать пробелы вокруг = для параметров ключевого слова. Так что вместо:

    print("/", end = " ")
    

    рекомендуется использовать:

    print("/", end=" ")
    

    Встроенный перевод

    Одна из моих любимых и часто упускаемых из виду функций — str.translate(). Он используется для перевода символов в строке, что вы и пытаетесь здесь сделать. 'S' переводится на '...', и так далее.

    >>> MORSE = {'A': '.-', 'B': '-...', 'C': '-.-.', 'O': '---', 'S': '...'}
    >>> morse_table = str.maketrans(MORSE)
    >>> "SOS".translate(morse_table)
    '...---...'
    >>> 
    

    Проблема здесь в том, что все точки и тире сошлись вместе. Вы хотите, чтобы после каждого кода был пробел. Самое простое, что вы могли бы сделать, — это добавить пробел в коды.

    >>> MORSE = {'A': '.- ', 'B': '-... ', 'C': '-.-. ', 'O': '--- ', 'S': '... '}
    >>> morse_table = str.maketrans(MORSE)
    >>> "SOS".translate(morse_table)
    '... --- ... '
    >>> 
    

    Единственная проблема — встроенная функция перевода оставит без изменений любой символ, который не найден в таблице перевода. Это оставит неожиданные символы в вашем переводе.

    >>> "S.O.S!".translate(morse_table)
    '... .--- .... !'
    

    Восклицательный знак в конце оставлен без перевода. Хуже того, периоды остаются непереведенными, что делает .O в .---J), а также .S в .... (ан H). В translate функция удалит любые символы, которые переводятся на None, но поскольку список недопустимых символов огромен, добавление записи для каждого недопустимого символа нецелесообразно.

    Мы можем настроить нашу таблицу перевода так, чтобы она просто возвращала None для каждого неизвестного ключа:

    >>> class MorseTable(dict):
        def __missing__(self, key):
            return None
    
        
    >>> morse_table = MorseTable(morse_table)
    >>> "S.O.S!".translate(morse_table)
    '... --- ... '
    

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

    >>> set("SOS") <= MORSE.keys()
    True
    >>> set("S.O.S!") <= MORSE.keys()
    False
    

    Переработанный код

    import sys
    
    MORSE_CODE = {
        'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.',
        'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..',
        'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.',
        'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-',
        'Y': '-.--', 'Z': '--..',
        '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....',
        '6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----',
        ' ': "https://codereview.stackexchange.com/"
    }
    
    # Add spaces to codes while creating translation table.
    MORSE_TRANS_TABLE = str.maketrans({letter: code + " "
                                       for letter, code in MORSE_CODE.items()})
    
    def translate(message: str) -> str:
        """
        Translate a message string into Morse Code
    
        Raise a ValueError if the message contains non-Morse code characters.
    
        >>> translate("SOS")
        '... --- ...'
        """
    
        message = message.upper()
        if set(message) <= MORSE_CODE.keys():
            # Translate message and remove trailing space
            return message.translate(MORSE_TRANS_TABLE).strip()
        else:
            raise ValueError("Invalid Morse Code characters in message")
    
    if __name__ == "__main__":
        print("This program translates a message into to morse code.n")
    
        message = input("Enter a word or phrase to be encoded: ")
    
        try:
            print(translate(message))
        except ValueError:
            print("Invalid characters found - refer to morse code alphabet", file=sys.stderr)
    

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *