user12387415
user12387415

Reputation:

Morse Encoding with Binary Tree in Python

I am trying to code a program that encodes/decodes Morse Code using a binary tree. Initially my code worked by taking an input when run and successfully converted it to Morse code.

However, I am trying to make it so that the terminal command encode('xyz') will take the xyz string and convert to the Morse, returning the result. To do this, I have had to rejig the code, and am now running into errors. Is there something obvious I am missing?

class BTree: 
    def __init__(self, data=None, left=None, right=None):
        self.data = data
        self.left = left 
        self.right = right 

    def insert(self, data):
        if self.data:
            if data < self.data:
                if self.left:
                    self.left.insert(data)
                else: 
                    self.left = BTree(data) 
            elif data > self.data: 
                if self.right:
                    self.right.insert(data) 
                else:
                    self.right = BTree(data) 
        else:
            self.data = data

    def output(self): 
        if self.left:
            self.left.output() 
        print(self.data) 
        if self.right: 
            self.right.output() 

    def encode(self, msg: str):
        dotsdashes = []
        code = "".join(dotsdashes)

        for character in msg:
            self.getMorse(code, character, dotsdashes)
            morseCode = morseCode + code + " "

    def getMorse(self, node, character, code):
        if node==None:
            return False
        elif node.data == character:
            return True
        else:
            if self.getMorse(node.left,character,code) == True:
                code.insert(0,".")
                return True
            elif self.getMorse (node.right,character,code)==True:
                code.insert(0,"-")
                return True 

My main function (won't format properly when copied and pasted as one):

if __name__ == '__main__':
    root = BTree("Root")
    root.left = BTree("E")
    root.right = BTree("T")
    root.left.left = BTree("I")
    root.left.right = BTree("A")
    root.right.left = BTree("N")
    root.right.right = BTree("M")
    root.left.left.left = BTree("S")
    root.left.left.right = BTree("U")
    root.left.right.left = BTree("R")
    root.left.right.right = BTree("W")
    root.right.left.left = BTree("D")
    root.right.left.right = BTree("K")
    root.right.right.left = BTree("G")
    root.right.right.right = BTree("O")
    root.left.left.left.left = BTree("H")
    root.left.left.left.right = BTree("V")
    root.left.left.right.left = BTree("F")
    root.left.left.right.right = BTree("")
    root.left.right.left.left = BTree("L")
    root.left.right.left.right = BTree("")
    root.left.right.right.left = BTree("P")
    root.left.right.right.right = BTree("J")
    root.right.left.left.left = BTree("B")
    root.right.left.left.right = BTree("X")
    root.right.left.right.left = BTree("C")
    root.right.left.right.right = BTree("Y")
    root.right.right.left.left = BTree("Z")
    root.right.right.left.right = BTree("Q")
    root.right.right.right.left = BTree("")
    root.right.right.right.right = BTree("")

Also wondering if populating the binary tree in the main function rather than it's own function was the right thing to do?

I'm relatively new to using binary trees so apologies for the potentially poor explanation/silly question.

EDIT: I should have included that the most recent error I am getting is: line 41, in getMorse elif node.data == character: AttributeError: 'str' object has no attribute 'data'

Upvotes: 0

Views: 1015

Answers (1)

trincot
trincot

Reputation: 350310

There are these issues:

  • In morseCode = morseCode + code + " " you reference morseCode on the right side of the assignment, but it was never initialised with a value. So you need to initialise it before the loop with the empty string

  • In the call self.getMorse(code, character, dotsdashes) you pass a string code as first argument, but the function expects a node there. So you should pass self as argument instead of code

  • self.getMorse returns a boolean, but in the above call you never look at this boolean. You should probably do something when that returned value is false.

  • code = "".join(dotsdashes) is happening too soon (and only once). This should happen in the loop after you made the call to self.getMorse as only then dotsdashes will have been populated.

  • dotsdashes = [] should happen each time before you call self.getMorse, as otherwise it will just accumulate the new morse code adjacent to what it already had from a previous iteration.

  • encode should return the result.

Here is a correction of that encode function:

    def encode(self, msg: str):
        morseCode = ""  # Init
        for character in msg:
            dotsdashes = []  # Moved inside the loop
            success = self.getMorse(self, character, dotsdashes) # Change first argument
            if not success:  # Deal with return value
                raise ValueError(f"Invalid character - no Morse code available for {character}")
            code = "".join(dotsdashes)  # Moved inside the loop
            morseCode = morseCode + code + " "
        return morseCode  # Return

Remarks:

  • It seems overkill to really create a tree for this. A dictionary seems more appropriate: it requires less code and will not need to walk through a tree to find a character, which could be anywhere.
  • It is not best practice to pass a list as third argument to self.getMorse, which it populates. Better is to make that the return value, and to return None if there is no Morse code available.
  • The insert method implements a binary search tree, which is not what you need here. It should not be included in your class.

Alternative tree

The tree you have created is a complete binary tree (even a perfect one), so you can encode that tree in a list (as is often done for binary heaps). And since the data for each node is just a character, it could be a string instead of a list.

The binary representation of an index in that string could then be made to be the Morse code (where 0,1 maps to dot,dash respectively) of the character at that index. To make sure that all binary 0 are included in the binary representation, even when they occur before any 1, we can go for binary representations where the first 1 is ignored, and all other binary digits represent the actual Morse code.

This leads to the following code, which is still tree-based:

MORSE = "..ETIANMSURWDKGOHVF.L.PJBXCYZQ"

def encode_char(c):
    return bin(MORSE.index(c))[3:].replace("0", ".").replace("1", "-")

def encode(s):
    return " ".join(map(encode_char, s))

print(encode("SOS"))

Upvotes: 3

Related Questions