HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonModerate

Python implementation of a Morse code translator

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
pythoncodeimplementationmorsetranslator

Problem

Here is my solution to reddit.com/r/dailyprogrammer #93 (easy)., which is a Python script that translates between English and Morse code.

Is String_To_Translate a good example of a class? Am I violating convention? How can I improve this code and make it more readable and pythonic?

```
import re

morse_code_dict = { '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':'-----',
',':'--..--',
'.':'.-.-.-',
'?':'..--..',
'/':'-..-.',
'-':'-....-',
'(':'-.--.',
')':'-.--.-',
' ':' ',
}

class String_To_Translate:
def __init__(self, string):
self.string = string
self.Translated = ''
self.lang = ''

def determine_lang(self):
exp = r'[a-zA-Z\,\?\/\(\)]'#regexp for non morse code char
if re.search(exp, se

Solution

-
The first thing that stands out from your code is the big table giving the morse code: it's lengthy, hard to read, and hard to check. (And consequently, it contains two mistakes.) I would write something like this:

from itertools import izip

def pairwise(s):
    """
    Generate pairs from the sequence `s`.

    >>> list(pairwise('abcd'))
    [('a', 'b'), ('c', 'd')]
    """
    it = iter(s)
    return izip(it,it)

morse_encoding = dict(pairwise('''
    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 -----

    , --..--  . .-.-.-  ? ..--..  / -..-.   - -....-
    ( -.--.   ) -.--.-  ' .----.  ! -.-.--  & .-...
    : ---...  ; -.-.-.  = -...-   + .-.-.   _ ..--.-
    " .-..-.  $ ...-..- @ .--.-.
'''.split()))

morse_decoding = {v:k for k,v in morse_encoding.iteritems()}


-
You rebuild the decoding table every time you start a new translation. This seems wasteful: I suggest building it only once, as above.

-
Not everything needs to be a class. A class is a data abstraction whose instances represent some thing together with methods for operating on that thing. You don't actually have any persistent data here: all you are using the class for is to store the temporary state for a single translation). It makes more sense to structure this code as functions.

-
Your classes and methods have no docstrings and no doctests. Even a small doctest or two might have caught the mistakes in your Morse code table.

-
In your determine_lang method, you use a regular expression that matches strings that are not Morse code. It would be simpler to do the test the other way round: to determine that the string is Morse code (because Morse code contains only dots, dashes and spaces). Python's sets provide a clear and concise way to express this test:

def is_morse_code(s):
    """
    Return True if the string `s` resembles Morse code (contains only
    dots, dashes and spaces), False otherwise.

    >>> is_morse_code('... --- ...')
    True
    >>> is_morse_code('abc')
    False
    """
    return set(s) <= set('.- ')


-
The original challenge may have referred to "English", but there's nothing actually English-specific about Morse code. It translates any language that can be written in the Latin alphabet. So I would prefer names that didn't presume that we were translating English.

-
When a character cannot be encoded or decoded, you print a message to inform the user. The usual approach in Python is to raise an exception.

-
Your translation code seems very complex for what it is doing. All you need to do is split the string into words, split each word into letters, translate each letter, join the translated letters into words, and then join the translated words into the result. This can be done neatly and concisely using split and join and a couple of loops:

def morse_encode(s):
    """
    Encode the string `s` in Morse code and return the result. 
    Raise KeyError if it cannot be encoded.

    >>> morse_encode('morse code')
    '-- --- .-. ... .  -.-. --- -.. .'
    """
    return '  '.join(' '.join(morse_encoding[l] for l in word)
                    for word in s.lower().split(' '))

def morse_decode(s):
    """
    Decode the string `s` from Morse code and return the result.
    Raise KeyError if it cannot be decoded.

    >>> morse_decode('-- --- .-. ... .  -.-. --- -.. .')
    'morse code'
    """
    return ' '.join(''.join(morse_decoding[l] for l in word.split(' '))
                    for word in s.split('  '))

Code Snippets

from itertools import izip

def pairwise(s):
    """
    Generate pairs from the sequence `s`.

    >>> list(pairwise('abcd'))
    [('a', 'b'), ('c', 'd')]
    """
    it = iter(s)
    return izip(it,it)

morse_encoding = dict(pairwise('''
    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 -----

    , --..--  . .-.-.-  ? ..--..  / -..-.   - -....-
    ( -.--.   ) -.--.-  ' .----.  ! -.-.--  & .-...
    : ---...  ; -.-.-.  = -...-   + .-.-.   _ ..--.-
    " .-..-.  $ ...-..- @ .--.-.
'''.split()))

morse_decoding = {v:k for k,v in morse_encoding.iteritems()}
def is_morse_code(s):
    """
    Return True if the string `s` resembles Morse code (contains only
    dots, dashes and spaces), False otherwise.

    >>> is_morse_code('... --- ...')
    True
    >>> is_morse_code('abc')
    False
    """
    return set(s) <= set('.- ')
def morse_encode(s):
    """
    Encode the string `s` in Morse code and return the result. 
    Raise KeyError if it cannot be encoded.

    >>> morse_encode('morse code')
    '-- --- .-. ... .  -.-. --- -.. .'
    """
    return '  '.join(' '.join(morse_encoding[l] for l in word)
                    for word in s.lower().split(' '))

def morse_decode(s):
    """
    Decode the string `s` from Morse code and return the result.
    Raise KeyError if it cannot be decoded.

    >>> morse_decode('-- --- .-. ... .  -.-. --- -.. .')
    'morse code'
    """
    return ' '.join(''.join(morse_decoding[l] for l in word.split(' '))
                    for word in s.split('  '))

Context

StackExchange Code Review Q#15228, answer score: 10

Revisions (0)

No revisions yet.