Reputation:
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
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:
self.getMorse
, which it populates. Better is to make that the return value, and to return None
if there is no Morse code available.insert
method implements a binary search tree, which is not what you need here. It should not be included in your class.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