def not_handler(formula): ''' (str) -> FormulaTree REQ: fomula[0] == '-' Returns the FormulaTree where NOT is the root of the tree Returns None iff the NOT formula is invalid >>> not_handler('-(x+y)') NotTree(OrTree(Leaf('x'), Leaf('y'))) >>> None == not_handler('-+x') True ''' # A formula with a Not in front has 4 cases # case 1 after the NOT there is a variable if formula[1] in VARIABLES: # then return the NotTree + the Leaf tree = NotTree(Leaf(formula[1])) # case 2 after the NOT there is a left bracket elif formula[1] is LEFT_P: # then call binary_handler tree = binary_handler(formula[1:]) # check if the binary tree is valid if it is not we do not evaluate it if tree is not None: # not the resulting Binary tree tree = NotTree(tree) # case 3 after the NOT there is another NOT bracket elif formula[1] is NOT: # then call NOT Handler again tree = not_handler(formula[1:]) # same case applies if tree is not None: tree = NotTree(tree) # anything else and the formula rules are violated so return None else: tree = None return tree
def build_tree_helper1(formula): '''(list)-> list Return the formula tree in the list >>>build_tree_helper1(['(', 'x', '*', 'y', ')']) [AndTree(Leaf('x'), Leaf('y'))] >>>build_tree_helper1(['(', '(', 'x', '+', 'y', ')', '*', '(', '-', 'x', '*', 'y', ')', ')']) [AndTree(OrTree(Leaf('x'), Leaf('y')), AndTree(NotTree(Leaf('x')), Leaf('y')))] ''' # return itself if just a variable inside list if len(formula) == 1: return formula else: # find the first ')' for i in range(len(formula)): if formula[i] == ')': break # find the first'(' in the list from the first ')' for k in range(i, -1, -1): if formula[k] == '(': break # find the AND or OR sign between the '()' for m in range(k, i + 1): if (formula[m] == '*') or (formula[m] == '+'): break # set the default of left and right child as Leaf left_node = Leaf(formula[m - 1]) right_node = Leaf(formula[i - 1]) # if the right or left child is already a formulatree then # itself is the child and it is not leaf if type(formula[m - 1]) != str: left_node = formula[m - 1] if type(formula[i - 1]) != str: right_node = formula[i - 1] # find NOT sign if it exist and call Not_tree_helper() function # to converte it to formatetree if ((formula[m - 2]) == '-'): left_node = NotTree(Not_tree_helper(formula[k + 2:m])) if ((formula[m + 1]) == '-'): right_node = NotTree(Not_tree_helper(formula[m + 2:i])) # find the sign and use the right formatetree class to converte it if (formula[m] == '*'): formula[k:i + 1] = [AndTree(left_node, right_node)] formula = build_tree_helper1(formula[:]) elif (formula[m] == '+'): formula[k:i + 1] = [OrTree(left_node, right_node)] formula = build_tree_helper1(formula[:]) return formula
def build_tree(formula): '''(str of formula) -> obj of FormulaTree This function takes in a string of formula and returns the FormulaTree that represents <formula> if it is a valid formula. Return None if the formula is not valid >>> build_tree('x') Leaf('x') >>> build_tree('-x') NotTree(Leaf('x')) >>> build_tree('(x+y)') OrTree(Leaf('x'), Leaf('y')) ''' # default the result to be None # if it doesn't change the result (not go into any if statements), # then formula is invalid res = None # check if the formula is an empty string, b/c I need to use formula[0] if formula == '': # formula is invalid # do nothing since setted default of the result pass # base case when there is only one valid variable inside, like: 'x' elif len(formula) == 1 and formula.islower(): # then it is a Leaf res = Leaf(formula) # check if the formula starts with '-' elif formula[0] == '-': # then it is a NotTree # keep recursing the formula after '-' res = NotTree(build_tree(formula[1:])) # check if the formula starts with '(' elif formula[0] == '(' and formula[-1] == ')': # then it is an OrTree or AndTree # use get_root_index helper, find the index of the root in formula index = get_root_index(formula) # if we find the root (valid formula) if index != len(formula) - 1: # if the root is '+' if formula[index] == '+': # then it is an OrTree # split the formula into left child and right child # left child is on the left hand side of the '+' and vice versa res = OrTree(build_tree(formula[1:index]), build_tree(formula[index + 1:-1])) # if the root is '*' elif formula[index] == '*': # then it is an AndTree # split the formula into left child and right child # left child is on the left hand side of the '*' and vice versa res = AndTree(build_tree(formula[1:index]), build_tree(formula[index + 1:-1])) # if result have changed, but any of its child is None (formula invalid) if res and None in res.get_children(): # adjust the result to be None again res = None # return the result return res
def build_tree(formula): '''(str) -> FormulaTree If the str <formula> meets the criteria of being a valid formula, create and return the FormulaTree representation of the string. Otherwise returns None. REQ: formula is not empty >>> build_tree('x') Leaf('x') ''' # Check if formula is valid if is_valid(formula): # Base case when length of list is one if len(formula) == 1: tree = Leaf(formula) # Second base case is special case of the NotTree elif formula[0] == '-': # Recursively create the rest of the formula '--(..)' if formula[1] == '-': tree = NotTree(build_tree(formula[1:])) # Create the NotTree for the rest of the bracket '-(..)' elif formula[1] == '(': tree = NotTree(build_tree(formula[1:formula.index(')') + 1])) # Create the NotTree only for the variable elif formula[1].islower(): tree = NotTree(build_tree(formula[1])) else: # Get the root and the index of the root (r, index) = find_root(formula) # Build the 'And' and/or 'Or' trees respectively if r == '*': tree = AndTree(build_tree(formula[1:index]), build_tree(formula[index + 1:len(formula) - 1])) elif r == '+': tree = OrTree(build_tree(formula[1:index]), build_tree(formula[index + 1:len(formula) - 1])) else: tree = None return tree
def build_tree(formula): if len(formula) is 1 and formula in "abcdefghijklmnopqrstuvwxyz": node = Leaf(formula) else: if len(formula) > 1 and formula[0] == "(" and formula[-1] == ")": formula = formula[1:-1] connective_found = False counter = 0 found_L_brac = False while not connective_found: if counter >= len(formula): node = None connective_found = True elif found_L_brac and formula[counter] is ")": found_L_brac = False elif formula[counter] is "(": found_L_brac = True elif formula[counter] in "+" and not found_L_brac: (node) = OrTree(build_tree(formula[:counter]), build_tree( formula[counter + 1:])) if (node.get_children())[0] is None or node.get_children()[1] is None: node = None connective_found = True elif formula[counter] in "*" and not found_L_brac: (node) = AndTree(build_tree(formula[:counter]), build_tree( formula[counter + 1:])) if (node.get_children())[0] is None or node.get_children()[1] is None: node = None connective_found = True counter += 1 elif len(formula) >= 2 and formula[0] is "-": if formula[1] not in "abcdefghijklmnopqrstuvwxyz-(": connective_found = True node = None else: node = NotTree(build_tree(formula[1:])) if (node.get_children())[0] is None: node = None else: node = None return (node)
def Not_tree_helper(formula): '''(list) -> FormateTree Return the formateTree form by inputing just the NOT signs and it variable >>>Not_tree_helper(['-', '(', 'x', '*', 'y', ')']) Leaf('y') ''' # find all '-'s and usinf recursion to get the Formulatree format for n in range(0, len(formula), 1): if formula[n] == '-': result = NotTree(Not_tree_helper(formula[n + 1:])) elif (type(formula[n])) != str: result = formula[n] elif (formula[n].isalpha() is True): result = Leaf(formula[n]) return result
def build_tree(formula): ''' (string) -> FormulaTree This function takes in a string representation of formula. Returns the FormulaTree which represents the given formula if vaild. Otherwise None is returned. REQ: the formula given must be a string representation ''' # idea: this functon use recursion to be efficient # apart the formula into small parts and build each leaf ''' this is a fail part for testing valid formula symbol_list = "xyz+-*()" for char in formula: if symbol_list.find(char) != -1: pass else: return None ''' # the base case of a leaf on the tree is that only one lowercase letter if len(formula) == 1 and formula[0].islower(): # build one leaf of the tree return Leaf(formula) # if the formula starts with "-", it means a not tree elif formula[0] == "-": # build a NotTree and send the rest of the formula into the recursion return NotTree(build_tree(formula[1:])) # for the symbol "+", "*", there must be a blanket before and after them elif formula[0] == "(": # here calls a helper function to find the place where the first symbol in the blanket is index = _get_place(formula) # build an AndTree, remove the blanket # apart the formula to two parts: before the "*" and after # send both parts into the recursion to get result for the AndTree if formula[index] == "*": return AndTree(build_tree(formula[1:index]), build_tree(formula[index + 1:-1])) # same step of "+" as the "*" above elif formula[index] == "+": return OrTree(build_tree(formula[1:index]), build_tree(formula[index + 1:-1])) # 'else' means the formula is not valid, therefore None is returned as required else: return None
def build_tree(formula): '''(str) -> FormulaTree given a string formula, return the FormulaTree representation of the formula if it is a valid formula. Otherwise return None. *, +, - represent AND OR NOT respectively. A node that represents * or + has 2 child nodes, which in turn represent the two subformulas that are ANDed or ORed to form the larger formula. A node that represents - has one child node, which in turn represents the formula that is NOTed. REQ: - formula is not empty - the variables in the formula must be lower case - the representation of the formula must contain enough paranthesis(except for '-' >>> build_tree('X') None >>> build_tree('x*y') None >>> build_tree('-(x)') None >>> build_tree('(x+(y)*z)') None >>> build_tree('((-x+y)*-(-y+x))') AndTree(OrTree(NotTree(Leaf('x')), Leaf('y')), NotTree(OrTree(NotTree(Leaf('y')), Leaf('x')))) >>> build_tree('(x*y)') AndTree(Leaf('x'), Leaf('y')) >>> build_tree('-y') Leaf('y') >>> build_tree('x') Leaf('x') >>> build_tree('v') Leaf('v') >>>build_tree('((x+y)*(z+-k))') AndTree(OrTree(Leaf('x'), Leaf('y')), OrTree(Leaf('z'), NotTree(Leaf('k')))) ''' try: # set the upper case checker as False upper = False # create lists tokenlist = [] var = [] # loop through the formula do determine the symbols for element in formula: # if the formula does not contain a paranthesis: if '(' not in formula: return None # if the element is '-' elif element == "-": # append the character to the list tokenlist.append(element) # if the element is '+' elif element == "+": # append the character to the list tokenlist.append(element) # if the element is '(' elif element == "(": # append the character to the list tokenlist.append(element) # if the element is '*' elif element == "*": # append the character to the list tokenlist.append(element) # if the element is ')' elif element == ")": # remove and return the last object from the list and set it # as a new variable token = tokenlist.pop() # loop through that variable while token != '(': # if the object is - if token == "-": # set the object as a variable, return the last object # from the list to determine the character var_formula = var.pop() # append it to the list by using NotTree since the # object has '-' var.append(NotTree(var_formula)) token = tokenlist.pop() # if the object is * elif token == "*": # set the left side of the * var_formula = var.pop() # now set the right side of the * var_2 = var.pop() # append it to the list by using And Tree class, since # the symbol is '*' var.append(AndTree(var_2, var_formula)) # remove and return the last item from the list token = tokenlist.pop() # if the object is '+' elif token == "+": # if the last item is '-' if tokenlist[-1] == "-" and tokenlist[-1] is not None: # set the left side of the '+' var_formula = var.pop() # set the right side of the '+' var_2 = var.pop() # since the object is '-', set it as NotTree var_2 = NotTree(var_2) # append it to the list by using OrTree class var.append(OrTree(var_2, var_formula)) # pop the object twice/ remove and return the last # item do it twice because of the '-' symbol token = tokenlist.pop() token = tokenlist.pop() # otherwise else: # set the left side var_formula = var.pop() # set the right side var_2 = var.pop() # append it to the list by using OrTree class var.append(OrTree(var_2, var_formula)) # remove and return the last object in the list token = tokenlist.pop() # if the element is a alphabetical character, elif element.isalpha() is True: # if the character is lower, if element.islower() is True: # then append to the list as 'Leaf' var.append(Leaf(element)) # otherwise return None else: upper = True return None # if the character is ')' elif element == ")": # remove and return the last object from token list and set it # as a variable token = tokenlist.pop() # loop through the list while token != '(': # if the object is '-' if token == "-": # remove and return the last object, that is next to # the '-' sign. var_formula = var.pop() # append the charachter as a NotTree because of the # '-' sign var.append(NotTree(var_formula)) # set a new variable that is the last object # from the list token = tokenlist.pop() # if the object is '*' elif token == "*": # remove and return the last object, the right side of # the '*' var_formula = var.pop() # now the left side of the '*' sign. var_2 = var.pop() # append the left side and right side by using AndTree # class var.append(AndTree(var_2, var_formula)) token = tokenlist.pop() # if the object is '+' elif token == "+": # if the last item is not none and '-' in the list, if tokenlist[-1] is not None and tokenlist[-1] == "-": # remove and return the last object, the right side # of the '+' var_formula = var.pop() # now do the same process for the left side var_2 = var.pop() # since the item is '-', we need to assign it as # NotTree var_2 = NotTree(var_2) # append the left side and right side by using # OrTree class var.append(OrTree(var_2, var_formula)) # pop the item twice/ remove and return the last # item do it twice because of the '-' symbol token = tokenlist.pop() token = tokenlist.pop() # otherwise else: # set the right side of the Or Tree var_formula = var.pop() # set the left side of the Or Tree var_2 = var.pop() # append the left side and right side by using # OrTree var.append(OrTree(var_2, var_formula)) # remove and return the last item in the list token = tokenlist.pop() # return the first item in the list return var[0] except IndexError: return None
def subtree_helper(formula): ''' (str, int) -> FormulaTree Returns the FormulaTree built for the formula <formula> REQ: length of the formula >= 1 >>> subtree = subtree_helper('(a+b)') >>> print(subtree) 'OrTree(Leaf('a'), Leaf('b'))' >>> subtree = subtree_helper('(-a)') >>> print(subtree) None >>> subtree = subtree_helper('(((a+b)+c)*c)') >>> print(subtree) AndTree(OrTree(OrTree(Leaf('a'), Leaf('b')), Leaf('c')), Leaf('c')) ''' # initialize your tree as none tree = None # 4 base cases: # 1- if the formula length is 0 if (len(formula) == 0): tree = None # 2- check if its a just a variable (and lower case). If it is, then make # that into a leaf elif (len(formula) == 1) and (formula[0].isalpha()) and \ (formula[0].islower()): tree = Leaf(formula[0]) # 3- if the formula doesn't start with a bracket or a # not operator (and isn't alpha + lower case either -> checked that in the # previous condition) then the tree is invalid -> return none elif not (formula.startswith('(') or formula.startswith('-')): tree = None # 4- check if its a negator. If it is, make a not tree and send the rest of # the string as a recursive call elif (formula[0] == '-'): # make the child of the Not Tree child = subtree_helper(formula[1:]) # if the child is a valid formula, then make a Not tree, else make the # tree None since the child is an invalid sub tree if child: tree = NotTree(child) # else the child is invalid, therefore the whole tree becomes invalid else: tree = None # else if it starts with an open bracket and ends with a closed bracket # then we have an AND tree or an OR tree elif (formula[0] == '(') and (formula[-1] == ')'): # then find the root of the tree (find root checks if we have a valid # root, otherwise would return none) root_index = find_root(formula) # if the root exists in the formula if (root_index is not None): # then make the left sub tree by making a recursive call on # everything from the left +1 (not including the bracket) to the # root left_subtree = subtree_helper(formula[1:root_index]) # similarly, make the right sub tree by making a recursive call on # everything from one character after the root till one index # before the string ends (we don't want the bracket, because it # will make the child invalid) right_subtree = subtree_helper(formula[root_index + 1:-1]) # check if the sub trees are valid sub trees, if they are then if (left_subtree and right_subtree): # (finally!) make the tree. If the root is a + sign make an OR # tree using the subtrees we have made if (formula[root_index] == '+'): tree = OrTree(left_subtree, right_subtree) # else if its a * sign make an add tree using the sub trees we # have made elif (formula[root_index] == '*'): tree = AndTree(left_subtree, right_subtree) # if its neither (and we handled the case where we have -) then # the tree is invalid because of an invalid symbol or placing # an valid symbol some where wrong. # therefore the whole tree becomes invalid/none else: tree = None # else the left and right sub trees are not valid sub trees. hence # making the entire tree invalid (aka none) else: tree = None # if the root does not exist, then there is no tree to make. Hence the # tree becomes none else: tree = None # else, the string is too complicated! just leave it as it is, and hope the # marker figures it out, lol # jk # else, the string is complicated. else: # so your find the root of the formula and split it from there. root_index = find_root(formula) # if there is a root in the formula if (root_index is not None): # make a recursive call on everything left from the root and left_subtree = subtree_helper(formula[:root_index]) # make a recursive call on everything from the right of the root right_subtree = subtree_helper(formula[root_index + 1:]) # if the left and right sub trees are valid if (left_subtree and right_subtree): # then make the tree based on the root # if it is a +, then make an or tree if (formula[root_index] == '+'): tree = OrTree(left_subtree, right_subtree) # else if its a *, then make an and tree elif (formula[root_index] == '*'): tree = AndTree(left_subtree, right_subtree) # else the operator is invalid, hence making the entire tree # invalid/None else: tree = None # else the left or right subtrees are invalid -> the whole tree is # invalid else: tree = None # if the root does not exist, then there is no tree to make. Hence the # tree becomes none else: tree = None # return the tree return tree
def build_tree(formula): '''(str) -> FormulaTree Builds a tree out of the formula given to the function an returns an obj representation of it. A proper tree does not have any capitalized variables, any extraneous parenthesses, unmatches parenthesses, or missing parenthesses. >>> build_tree('X') None >>> build_tree('(x*y)') AndTree(Leaf('x'), Leaf('y')) >>> build_tree("(x+y*(z))") None >>> build_tree("((-x+y)*-(-y+x))") AndTree(OrTree(NotTree(Leaf('x')), Leaf('y')), NotTree(OrTree(NotTree(Leaf('y')), Leaf('x')))) ''' # Set an arbitrary tree solely for error detection tree = NotTree(None) # Base case when the formula is empty if (len(formula) == 0): tree = None # Base case when the formula is just a variable/leaf elif (len(formula) == 1 and formula.islower()): tree = Leaf(formula) # Base case when the formula has a Not connective elif (len(formula) >= 2 and formula.startswith('-')): tree = NotTree(build_tree(formula[1:])) else: # Set the variables required for the while loop # i is the index, o_p_c stands for Open Parenthesses Count and # root found confirms when the outermost root is found, this variable # reduces the amount of iteration increasing overall efficency i, o_p_c, root_found = 0, 0, False # Loop through the entire string. The outermost root is found when the # o_p_c is 1; once its found, loop cancels. If the outermost root isn't # found by some unrecognizable form, the loop cancels and the tree # stays as the same arbitrary tree assigned earlier. while (root_found is False and i < len(formula)): # Increment o_p_c by 1 if an open parenthesses is found if (formula[i] == '('): o_p_c += 1 # Decrement o_p_c by 1 if a closed parenthesses is found elif (formula[i] == ')'): o_p_c -= 1 # Recur over the outermost Or connective when found; root is found elif (formula[i] == '+' and o_p_c == 1): tree, root_found = OrTree(build_tree(formula[1:i]), build_tree(formula[i + 1:-1])), True # Recur over the outermost And connective when found; root is found elif (formula[i] == '*' and o_p_c == 1): tree, root_found = AndTree(build_tree(formula[1:i]), build_tree(formula[i + 1:-1])), True # Increment index by 1 i += 1 # If statement to know when the tree is in an unrecognizable form if (tree is None): tree = None elif (None in tree.get_children()): tree = None # Return the built tree return tree
def validation_1(formula, index, length): '''(int) -> tuple of (FormulaTree, int) Given a formula, a index and the length the function check whether it is valid recursively. Then build it as formulatree, return the tuple of formula tree and its index if it is valid, otherwise, return None >>> formula = "x+(-y)" >>> index = 0 >>> length = 6 >>> validation_1(formula, index, length) None >>> formula = "-----a" >>> index = 0 >>> length = 6 >>> validation_1(formula, index, length) NotTree(NotTree(NotTree(NotTree(NotTree(Leaf('a')))))) >>> formula = "x" >>> index = 0 >>> length = 1 >>> validation_1(formula, index, length) Leaf('x') >>> formula = "-y" >>> index = 0 >>> length = 2 >>> validation_1(formula, index, length) NotTree((Leaf('y')) ''' # define a result which equal to None result = None # if the it is not the end of formula according to the index if index != length: # get the letter of the string in the index ch = formula[index] # check whether it is in lower letters if ch in 'abcdefghijklmnopqrstuvwxyz': # if it is build it as leaf, and continuethe index for checking result = Leaf(ch), index + 1 # else if the letter of string in the index is the left bracket elif ch == '(': # call the validation2 function to check whether the stuff in the # brackets is valid ret = validation_2(formula, index + 1, length) # if it is valid if ret is not None: # get the tuple of the operand and the index operand, index = ret # if it is not the end of formula if index != length: # if the letter of string in the index is right bracket if formula[index] == ')': # get the operand and continue index for checking result = operand, index + 1 # else if letter of string in the index is "-" elif ch == '-': # recurse the validation1 for checking whether the stuff is valid # after the "-" symbol ret = validation_1(formula, index + 1, length) # if it is valid if ret is not None: # get the tuple of operand and the index operand, index = ret # build the operand as not tree and make tuple with # current index result = NotTree(operand), index return result
def build_tree(formula): ''' (str) -> FormulaTree Returns True if the <formula> is valid. Else returns False. How is a formula valid? > all the variable names should be lower case > there can be only be 26 unique varaibles, aka the total number of alphabets in english language > the only valid operators are the AND ('*'), the NOT ('-') and the OR ('+') operator. > when using the AND ('*') and OR ('+') operator, it is necessary to use parentheses. e.g. '(x*y)' is a valid formula, but 'x*y' is not a valid formula because it is missing parentheses > Parentheses are not needed when using the NOT ('-') operator. e.g. '-x' is a valid formula, but '-(x)' is not a valid formula because it has extraneous brackets > the simplest formula is of a string containing just one variable. e.g. 'x' is the simplest formula Any formula that does not follow the above rules is an invalid formula REQ: <formula> can not be an empty string REQ: <formula> only has symbols '*', '-', '+' and all the lower case letters from a - z >>> build_tree('-x') NotTree(Leaf('x')) >>> build_tree('-(x)') None >>> build_tree('-(x*y)') NotTree(AndTree(Leaf('x'), Leaf('y'))) >>> build_tree('x*y') None >>> build_tree('x') Leaf('x') ''' # initialize an empty tree tree = None # find the root using the root helper function root_index = find_root(formula) # if the root exists, if root_index is not None: # then see if the root is an OR operator if (formula[root_index] == '+'): # make the left sub tree (everything from the start to one index # before the root left_tree = subtree_helper(formula[1:root_index]) # make the right subtree (everything from one index after the root # to the end) right_tree = subtree_helper(formula[root_index + 1:-1]) # if the left sub tree or the right sub tree is not empty (meaning # its not invalid), then make an OR tree if (left_tree and right_tree): tree = OrTree(left_tree, right_tree) # else, left sub tree or right sub tree is invalid and therefore # makes the entire tree invalid. Hence the tree is none else: tree = None # else check if the root is an AND operator elif (formula[root_index] == '*'): # make the left sub tree (everything from the start to one index # before the root left_tree = subtree_helper(formula[1:root_index]) # make the right subtree (everything from one index after the root # to the end) right_tree = subtree_helper(formula[root_index + 1:-1]) # if the left sub tree or the right sub tree is not empty (meaning # its not invalid), then make an OR tree if (left_tree and right_tree): tree = AndTree(left_tree, right_tree) # else, left sub tree or right sub tree is invalid and therefore # makes the entire tree invalid. Hence the tree is none else: tree = None # else if the root is a NOT operator elif (formula[root_index] == '-'): # make the sub tree (everything from the root_index + 1 to the end # since not only has one child/ excluding the not operator) child = subtree_helper(formula[root_index + 1:]) # if the child is a valid tree if child: # then make a Not tree tree = NotTree(child) # else, the child is not valid, making the whole tree invalid/none else: tree = None # if the operator is not from +, -, *, then the operator is invalid # making the entire tree invalid else: tree = None # else if the str a lower case alphabet of len 1, then make it into a leaf # and return that as a tree elif (len(formula) == 1) and (formula.isalpha()) and (formula.islower()): tree = Leaf(formula) # else if the root does not exist, then the tree is invalid else: tree = None # return the tree that was built (or built but destroyed by the multiple # checks, oof!) return tree
def build_tree(formula): ''' (string) -> formula_tree This function takes a string and puts it into a proper formula tree. If the string is not in the correct format, then return None. >>> build_tree('-x') NotTree(Leaf('x')) >>> build_tree('((-x+y)*-(-y+x))') AndTree(OrTree(NotTree(Leaf('x')), Leaf('y')), \ NotTree(OrTree(NotTree(Leaf('y')), Leaf('x')))) >>> x = build_tree('Pancer Square') >>> x == None True ''' # find length of formula length = len(formula) # base case, if only one character if (length == 1): # return a leaf return Leaf(formula) # if a not symbol occurs if (formula[0] == "-"): # create NotTree and continue building return NotTree(build_tree(formula[1:])) # this means that the root will either be # a AndTree or OrTree if (formula[0] == '('): # create counters and initialize variables counter = 0 root = '' # to use in while loop x = 0 # this loop finds the root while x < len(formula) and root == '': # subtract 1 if rigth bracket is found if formula[x] == ")": counter = counter - 1 # if a not symbol is used if formula[x] == '-': x = x + 1 # add 1 if left bracket is found if formula[x] == "(": counter = counter + 1 # if the counter reaches 1 elif counter == 1: root = formula[x + 1] x = x + 1 # if root is a + if root == '+': return OrTree(build_tree(formula[1:x]), build_tree(formula[x + 1:-1])) # if the root is a * if root == '*': return AndTree(build_tree(formula[1:x]), build_tree(formula[x + 1:-1])) # in case of incorrect format if formula[0].isalpha() is False: return None
def build_tree(formula): '''(str) -> FormulaTree Takes in a string representation of a boolean formula, returns the corresponding FormulaTree representation of the formula, given that it is valid. If the formula is not valid, None is returned. Valid formulas are of one of the following types: F1 (F1 * F2) (F1 + F2) - F1 where F1 and F2 are also valid boolean formulas. REQ: all characters in formula are in VARIABLES and SYMBOLS >>> build_tree("x-y") is None True >>> build_tree("(-x+y)") OrTree(NotTree(Leaf('x')), Leaf('y')) >>> build_tree("((x*y)+z)") OrTree(AndTree(Leaf('x'), Leaf('y')), Leaf('z')) ''' # Defaulted the variable for the FormulaTree to an invalid input tree = None # Base Case, formula is empty: invalidate the formula if not formula: tree = None # Base Case, formula is a boolean variable elif formula in VARIABLES: tree = Leaf(formula) # Recursive Case 1: Formula is a negation of a boolean formula (NotTree) # Set that formula's children to be everything after the symbol elif formula[0] == '-': tree = NotTree(build_tree(formula[1:])) # Recursive Case 2: Formula is a boolean formula elif formula[0] == '(' and formula[-1] == ')': # Loop through the formula string ind = 1 while ind < len(formula) - 1: # Check if formula contains nested boolean formulas if formula[ind] == '(': brackets = 1 ind += 1 while brackets != 0 and ind < len(formula) - 1: # For each open bracket, add a new level of nesting if formula[ind] == '(': brackets += 1 # For each closing bracket, remove a level of nesting elif formula[ind] == ')': brackets -= 1 # Invalidate the tree if an invalid character is found elif not (formula[ind] in VARIABLES or formula[ind] in SYMBOLS): tree = None ind = len(formula) - 2 ind += 1 # Check if formula is an AndTree/OrTree formula # Set the formula's children to be everything before the symbol, # and everything after the symbol, then stop the while loop if formula[ind] == '*': tree = AndTree(build_tree(formula[1:ind]), build_tree(formula[ind + 1:-1])) ind = len(formula) - 1 elif formula[ind] == '+': tree = OrTree(build_tree(formula[1:ind]), build_tree(formula[ind + 1:-1])) ind = len(formula) - 1 ind += 1 # Check if there is an invalid formula nested in the boolean formula # The whole tree is then made invalid if 'None' in str(tree): tree = None return tree
def build_tree(formula): ''' (str) -> FormulaTree or NoneType Given a string representation of a formula, return a tree representation of it with FormulaTree objects, if the formula is invalid then return None instead >>> build_tree("") == None True >>> build_tree("(-x)") == None True >>> build_tree("x+y") == None True >>> build_tree("x*y") == None True >>> build_tree("x-y") == None True >>> build_tree("(x+2)") == None True >>> build_tree("X") == None True >>> build_tree("(x+y+z)") == None True >>> build_tree("(x+(y)+z)") == None True >>> build_tree("((x)(x+y))") == None True >>> build_tree("(((x+)(x+y)))") == None True >>> build_tree("x") == Leaf("x") True >>> build_tree("(n+m)") == OrTree(Leaf("n"), Leaf("m")) True >>> build_tree("(c*v)") == AndTree(Leaf("c"), Leaf("v")) True >>> build_tree("-x") == NotTree(Leaf("x")) True >>> build_tree("-((x+y)*(a*-b))") == NotTree(AndTree(OrTree(Leaf("x"),\ Leaf("y")), AndTree(Leaf("a"), NotTree(Leaf("b"))))) True ''' # If formula is 1 character, create a leaf from it if it's a valid # variable name, otherwise return None if (len(formula) == 1): if (formula.isalpha() and formula.islower()): result = Leaf(formula) else: result = None # If the formula is an empty string, it is invalid therefore return None elif (len(formula) == 0): result = None else: # If the first character is -, then first build the smaller subtree # and create a NotTree for the subtree if the subtree is valid if (formula[0] == "-"): child1 = build_tree(formula[1:]) if (child1 is None): result = None else: result = NotTree(child1) # if the first and last characters are brackets then elif (formula[0] == "(" and formula[-1] == ")" and formula.count("(") == formula.count(")")): # find the operator that accompanies the bracket pair index = find_operator_index(formula) # if the operator is found, build the smaller subtree called # child1 from the left, build the smaller subtree called child2 # from the right, and from those create an AndTree or # OrTree based on the operator. if (index != -1): child1 = build_tree(formula[1:index]) child2 = build_tree(formula[index + 1:-1]) if (child1 is None or child2 is None): result = None else: if (formula[index] == "*"): result = AndTree(child1, child2) else: result = OrTree(child1, child2) # if index is -1, it means there is no corresponding operator, # which means the formula is invalid else: result = None # If the leading character is neither a single variable, "-", # nor "(", then the formula is invalid else: result = None # Return the resulting root of the FormulaTree return result
def build_tree(formula): '''(str) -> FormulaTree Takes the infix expression represented by formula and outputs the corresponding formula tree. build_tree works with to_postfix in order to convert an equation into postfix notation and then create the tree. (probably the cleanest function I've ever written) build_tree also uses validate to make sure that a formula is valid before attempting to construct the tree. >>> build_tree('((a+b)*(x*m))')) AndTree(OrTree(Leaf('a'), Leaf('b')), AndTree(Leaf('x'), Leaf('m'))) >>> build_tree('-a') NotTree(Leaf('a')) >>> build_tree('(a+X)') None ''' # first, we need to validate the formula using our helper # validate function. If the function is valid, we can proceed # constructing the tree if validate(formula) == True: # first, let's convert our equation into postfix notation # using our to_postfix helper function fixedFormula = to_postfix(formula) # create an empty stack that we're gonna use to store # sub-expressions and connect them later stack = [] # looping through every character in the postfix expression for character in fixedFormula: # if we encounter an operand, make a new subtree #we'll make the parent-child links as we go if character in letters: newTree = Leaf(character) # if we encounter an and operator, we know that # we have to have a left and right child, because and # is binary, so pop two characters from the stack # and set them to the left and right children of the # AndTree we're constructing. We're guaranteed to have # two or more characters in the stack because of the way # postfix expression works. elif character == '*': right = stack.pop() left = stack.pop() newTree = AndTree(left,right) # because not is a unary operator, all we need to do # is set its one child to the subexpression that's # highest up in the stack elif character == '-': newTree = NotTree(stack.pop()) # an or tree behaves the same way as an and tree, it just needs # its own statement because we'll be using a different type of # tree elif character == '+': right = stack.pop() left = stack.pop() newTree = OrTree(left,right) # no matter what kind of sub-tree we've created, we need to re-add it back onto the stack # so that we can later create the parent-child relationships of that tree stack.append(newTree) # in the case where the formula is invalid, we know that we can set the newTree to None and simply # return that valud else: newTree = None # return the tree represnted by the given equation return newTree
def build_tree(formula): '''(str) -> FormulaTree or NoneType Returns the root of the equivalent FormulaTree that represents the given formula. Said formula must be a vaid formula, and if not, NoneType is instead returned. >>> formula_1 = 'x' >>> expected_1 = "Leaf('x')" >>> result_1 = str(build_tree(formula_1)) >>> expected_1 == result_1 True >>> formula_2 = "(x*-c)" >>> expected_2 = "AndTree(Leaf('x'), NotTree(Leaf('c')))" >>> result_2 = str(build_tree(formula_2)) >>> expected_2 == result_2 True >>> formula_3 = "((x+(z*e))+-x)" >>> expected_3 = "OrTree(OrTree(Leaf('x'), AndTree(Leaf('z'),\ Leaf('e'))), NotTree(Leaf('x')))" >>> result_3 = str(build_tree(formula_3)) >>> expected_3 == result_3 True >>> formula_4 = "-((x+(a+c))+((b+d)*f))" >>> expected_4 = "NotTree(OrTree(OrTree(Leaf('x'), OrTree(Leaf('a'),\ Leaf('c'))), AndTree(OrTree(Leaf('b'), Leaf('d')), Leaf('f'))))" >>> result_4 = str(build_tree(formula_4)) >>> expected_4 == result_4 True >>> formula_5 = "(-(x+(x*f))*(((z+y)*-g)*((h*d)+(z*-b))))" >>> expected_5 = "AndTree(NotTree(OrTree(Leaf('x'), AndTree(Leaf('x'),\ Leaf('f')))), AndTree(AndTree(OrTree(Leaf('z'), Leaf('y')),\ NotTree(Leaf('g'))), OrTree(AndTree(Leaf('h'), Leaf('d')),\ AndTree(Leaf('z'), NotTree(Leaf('b'))))))" >>> result_5 = str(build_tree(formula_5)) >>> expected_5 == result_5 True >>> formula_6 = "(((-x*-y)*-h)+-(-f*((z+(g+c))+((z+-a)+(((e*c)+e)*\ (f+(a+g)))))))" >>> expected_6 = ("OrTree(AndTree(AndTree(NotTree(Leaf('x')),\ NotTree(Leaf('y'))), NotTree(Leaf('h'))),\ NotTree(AndTree(NotTree(Leaf('f')), OrTree(OrTree(Leaf('z'),\ OrTree(Leaf('g'), Leaf('c'))), OrTree(OrTree(Leaf('z'),\ NotTree(Leaf('a'))), AndTree(OrTree(AndTree(Leaf('e'), Leaf('c')),\ Leaf('e')), OrTree(Leaf('f'), OrTree(Leaf('a'), Leaf('g')))))))))") >>> result_6 = str(build_tree(formula_6)) >>> expected_6 == result_6 True >>> formula = "(((-x*-y)*-h)+-(-f*((z+(g+c))++((z+-a)+(((e*c)+e)*\ (f+(a+g)))))))" >>> expected_7 = None >>> result_7 = build_tree(formula) >>> expected_7 == result_7 True ''' # Assume that the formula is void result = None # Obtain the length of the formula formula_len = len(formula) # If the formula has 1 character and is a lowercase letter if ((formula_len == 1) and (formula in LOWERCASE_LETTERS)): # A Leaf node that contains the character (variable) is created result = Leaf(formula) # Else, if the formula has 2 characters, where the 1st character is '-' and # the 2nd character is a lowercase letter elif ((formula_len == 2) and ((formula[0] == NOT) and (formula[-1] in LOWERCASE_LETTERS))): # A Not node connected to a leaf node that contains the 2nd character # (variable) is created result = NotTree(Leaf(formula[-1])) # Else, if the formula has more than 2 characters elif (formula_len > 2): # Obtain the 1st character from the formula first_char = formula[0] # If the 1st character is '-' if (first_char == NOT): # Create a Not node: # The Not node's child will be the recursive call to the function, # with the 1st character removed from the formula sub_tree = build_tree(formula[1:]) result = NotTree(sub_tree) # If the Not node's child does not exist if (not sub_tree): # This formula is void result = None # Else, if the formula is closed by brackets elif ((first_char == OPEN_BRACKET) and (formula[-1] == CLOSED_BRACKET)): # Remove those brackets formula = formula[1:-1] # Update the length of the formula formula_len = len(formula) # Get the indices of '+' or '*' in the formula that allows the # formula to be split into 2 sub formulas: # Holds all the indices where we have possibly found '+' or '*' # that satisfies the above condition indices_of_interest = set() # A formula can be split into 2 sub formulas if when either '+' or # '*' is found, we are not in a sub formula. Therefore, we must # have balanced brackets when either '+' or '*' is found sub_formulas = 0 # Go through each character in the formula for index in range(formula_len): # Obtain the current character char = formula[index] # If the character is '(' if (char == OPEN_BRACKET): # We have entered a sub formula sub_formulas += 1 # Else, if the character is ')' elif (char == CLOSED_BRACKET): # We have exited a sub formula sub_formulas -= 1 # Else, if the character is either '+' or '*', and we are not # in any sub formulas elif (((char == OR) or (char == AND)) and (sub_formulas == 0)): # Add the index to our set indices_of_interest.add(index) # If the set of indices is not empty, and does not contain more # than 1 entry if ((indices_of_interest) and (len(indices_of_interest) == 1)): # Obtain the index of interest to split the formula into 2 # sub formulas index_of_interest = indices_of_interest.pop() # Create either a Or node or a And node: node = CONNECTIVES[formula[index_of_interest]] # The specified node's left child will be the recursive call to # the function, with the string slice to the left of where # either '+' or '*' was found in the formula left_sub_tree = build_tree(formula[:index_of_interest]) # The specified node's right child will be the recursive call # to the function, with the string slice to the right of where # either '+' or '*' was found in the formula right_sub_tree = build_tree(formula[index_of_interest + 1:]) result = node(left_sub_tree, right_sub_tree) # If either left or right child does not exist if ((not left_sub_tree) or (not right_sub_tree)): # This formula is void result = None # Returns the FormulaTree equivalent to the given formula return result
def build_tree_helper(formula, start, end): ''' (str) -> FormulaTree takes string <formula> and returns the FormulaTree representing the formula. returns None is there are invalid bracketing end is inclusive >>> >>> True ''' # if no conditions are met result = None # base case/ charcter check: if (start == end): if (formula[start].isalpha() and formula[start].islower()): # result is a leaf node with var result = Leaf(formula[start]) # base case, formula starts with elif (formula[start] == '-'): sub_result = build_tree_helper(formula, start + 1, end) if sub_result is None: result = None else: result = NotTree(build_tree_helper(formula, start + 1, end)) # else: else: # vars to keep track of '(' and ')' left_bracket_count = 0 right_bracket_count = 0 exit_loop = False i = start # find the most outer brackets and the connective associated with them while (i <= end and not exit_loop): bracket_difference = left_bracket_count - right_bracket_count if (formula[i] == '('): left_bracket_count += 1 elif (formula[i] == ')'): right_bracket_count += 1 if bracket_difference < 0: exit_loop = True # connective count to throw exceptions**************** elif ((formula[i] == '*' or formula[i] == '+') and bracket_difference <= 1): # might not need this one connective = formula[i] # use divide and conquer, and build the tree left = build_tree_helper(formula, start + 1, i - 1) right = build_tree_helper(formula, i + 1, end - 1) if (left is None or right is None): result = None elif (connective == '*'): result = AndTree(left, right) elif (connective == '+'): result = OrTree(left, right) # exit the rest of the while loop exit_loop = True # increment the loop i += 1 return result
def build_tree(formula): ''' (str) -> FormulaTree The function will create a FormulaTree based on the input formula str. The input str has to be in the right form otherwise, return None. The function will return a root of FormulaTree if input formula is in the valid form. The input str has to be in a valid form, which is: 1. "+" and "*" connect to leaf and arounded by "(" and ")" 2. for "-", there is no need for parenthesis. The function will start at an empty root, when we have a "(", we will create a left child, if we have a leaf, we will create that leaf and then go back to its parent and continune. When we have a ")" we will go back to parent. It has used concept of stack, which is a list to store parents. The function will change input formula into a good form, which is for each "-" sign, we have a matching ")" after its leaf. For example "-x" will change to "-x)"; "-(-x+y)" will change to "-(-x)+y))" >>> build_tree('a+b') == None True >>> build_tree('(a)') == None True >>> build_tree('-(a)') == None True >>> build_tree('(a+b+c)') == None True >>> tree = build_tree('(b+(c+d))') >>> tree == OrTree(Leaf('b'), OrTree(Leaf('c'), Leaf('d'))) True >>> tree = build_tree('(x+y)') >>> tree == OrTree(Leaf('x'), Leaf('y')) True >>> tree = build_tree('-(--x+y)') >>> tree == NotTree(OrTree(NotTree(NotTree(Leaf('x'))), Leaf('y'))) True >>> tree = build_tree('------x') >> tree == NotTree(NotTree(NotTree(NotTree(NotTree(NotTree(Leaf('x'))))))) True ''' # create a list to store all parents stack = [] # create an empty tree, add it into stack and let curr be it tree = FormulaTree(None, []) stack.append(tree) curr = tree # try to do the following try: # change formula into a good form for code formula = build_tree_helper(formula) # loop through each symbol in formula for s in formula: # if we have "(" if s == '(': # create a left subtree, and store parent in stack left = FormulaTree(None, []) curr.children.append(left) stack.append(curr) curr = curr.children[0] # if we have a laef elif s not in ['+', '*', '-', ')']: # if s is not alphabet or it is upper case, return None if not s.isalpha() or s.isupper(): return None # create a leaf of its symbol leaf = Leaf(s) # get the parent from stack parent = stack.pop() # if curr node has parent if curr in parent.children: # let parent pointer to this leaf index = parent.children.index(curr) parent.children[index] = leaf # let curr be parent (go back) curr = parent # if curr node has no parent else: # let tree be curr leaf tree = leaf # if after any leaf, stack is empty, then # it must be in invalid form, raise error if len(formula) != 1 and stack == []: raise SyntaxError # if we have "+", "*", or "-" elif s in ['+', '*', '-']: # get the parent from stack parent = stack[-1] # create a new node for its child new_node = FormulaTree(None, []) # if symbol is * then create AndTree if s == '*': replace = AndTree(curr.children[0], new_node) # if symbol is + then create OrTree elif s == '+': replace = OrTree(curr.children[0], new_node) # if symbol is - then create NotTree else: replace = NotTree(new_node) # if curr node has parent if curr in parent.children: # get the index and let parent pointer to curr node index = parent.children.index(curr) parent.children[index] = replace # get the new curr node curr = replace # if curr node has no parent else: # let tree be replace node tree = replace # get new curr node and reset stack curr = tree stack = [curr] # put curr into stack as parent and get next curr stack.append(curr) curr = curr.children[0] if s == '-' else curr.children[1] # if we have ")", then let curr go back to parent elif s == ')': curr = stack.pop() # if there is other situation, it must be error else: raise SyntaxError # if any error, then return None since it is an invalid form except: return None # if stack has anything which means invalid form, or # there are still some FormulaTree in result tree, return None if stack != [] or 'FormulaTree' in str(tree): return None return tree
def build_tree(formula): '''(str) -> FormulaTree or None take a string which represents the formula and return the FormulaTree when the formula is valid or None when the formula is invalid REQ: formula should be the right combinations of lower case latter, '-', '+', '*', '(' and ')' >>> build_tree("(x*y+z*x)") >>> build_tree('(x*y)') AndTree(Leaf('x'), Leaf('y')) ''' # create two list: # one is the list which stores the operators # the other is the list which stores the lower case letters operator = list() letter = list() # go through the formula and the formula need to be valid index = 0 is_valid = True while (index < len(formula) and is_valid is True): # the first element in formula[index:] is '(', '-', '+' or '*' if (formula[index] in ('(', '+', '-', '*')): # put this element into the operator list operator.append(formula[index]) # the first element in formula[index:] is lower case letter elif (formula[index].islower() is True): # the previous str is also a letter if (index > 0 and formula[index - 1].islower() is True): is_valid = False # the last element in operator list is '-' # that means '-' is the parent of the current leaf elif (len(operator) > 0 and operator[-1] == '-'): # pop the last element in the operator list # build a NotTree whose child is the current leaf # push this subtree to letter list operator.pop() letter.append(NotTree(Leaf(formula[index]))) # there is still symbol '-' exists in operator list while (len(operator) > 0 and operator[-1] == '-'): operator.pop() current_formula = letter.pop() letter.append(NotTree(current_formula)) # the last element in operator is not '-' else: # push this letter to letter list letter.append(Leaf(formula[index])) # the first element in formula[index:] is ')' elif (formula[index] == ')'): # the operator list is empty # or the letter list does not have two element if (len(operator) <= 1 or len(letter) < 2): is_valid = False else: # pop the last element in the operator list symbol = operator.pop() # pop two element from letter list # the last one is right child # and the second last one is left child right_child = letter.pop() left_child = letter.pop() # the symbol is '+' if (symbol == '+'): # build a OrTree for this subtree subtree = OrTree(left_child, right_child) # the symbol is '+' elif (symbol == '*'): # build a AndTree for this subtree subtree = AndTree(left_child, right_child) # the other symbol should be a invalid formula else: is_valid = False if (is_valid is True): # pop the last element in operator, it should be '(' # otherwise, it should be invalid previous_symbol = operator.pop() if (previous_symbol != '('): is_valid = False else: # the last element in operator is '-' # pop '-' and it is the parent of the subtree if (len(operator) > 0 and operator[-1] == '-'): operator.pop() # push this subtree to letter list letter.append(NotTree(subtree)) while (len(operator) > 0 and operator[-1] == '-'): operator.pop() current_formula = letter.pop() letter.append(NotTree(current_formula)) # the last element in operator is not '-' # push subtree to letter list straightly else: letter.append(subtree) # there are other symbol in the formula else: is_valid = False # increase the index index += 1 # the is_valid is True, the operator list is empty # and there is only one element in the letter list # the last element in the letter list is the FormulaTree if (is_valid is True and len(operator) == 0 and len(letter) == 1): result = letter.pop() else: result = None return result
def build_tree(formula): '''(str) -> FormulaTree Takes a string representing a formula. If the formula is valid, returns the FormulaTree representation of the formula, else returns None. There are some invalid formulas and reasons. "X" variable not lower case letter "x*y" missing parentheses "-(x)" extraneous parentheses "(x+(y)*z)" mismatched parentheses REQ: formula is a string representing a boolean formula. >>> build_tree('(x*y)') == AndTree(Leaf('x'), Leaf('y')) True >>> build_tree('(x+-y)') == OrTree(Leaf('x'), NotTree(Leaf('y'))) True >>> build_tree('(((x*y)*z)*w)') == AndTree(AndTree(AndTree(Leaf('x'), Leaf\ ('y')), Leaf('z')), Leaf('w')) True >>> build_tree('X') == None True >>> build_tree('x*y') == None True >>> build_tree('(x+(y)*z)') == None True ''' # set a condition for empty string empty = len(formula) == 0 # if given an empty string, it is invalid if (empty): # result is None result = None # if given a string containing 1 lowercase letter, it is valid elif (len(formula) == 1): # set a condition for single variable variable = formula.isalpha() and formula.islower() if (variable): # create a leaf of the letter result = Leaf(formula) # else it is invalid else: # result is None result = None else: # set a condition for Not operation no = formula[0] == "-" # set a condition for formula closed by parentheses # and the numbers of open parentheses and close parentheses are equal paired = formula[0] == "(" and formula[-1] == ")" and\ formula.count("(") == formula.count(")") # if - is the first character if (no): # build the subtree sub = build_tree(formula[1:]) # if the subtree is valid if (sub is not None): # create a NotTree of the subtree result = NotTree(sub) # else the subtree is invalid else: # result is None result = None # if parentheses are to begin and end the string, and they are paired elif (paired): # find the root operator i = build_help(formula) # if the operator doesn'turns exist if (i == -1): # result is None result = None # else the operator exists else: # build a left subtree of the string before the operator lc = build_tree(formula[1:i]) # build a right subtree of the string after the operator rc = build_tree(formula[i + 1:-1]) # if one of the strings before & after the operator is invalid if (lc is None) or (rc is None): # result is None result = None # else the strings are both valid else: # if the operator is + if (formula[i] == "+"): # create an OrTree result = OrTree(lc, rc) # if the operator is * elif (formula[i] == "*"): # create a AndTree result = AndTree(lc, rc) # else the first character is not a lowercase letter, -, nor ( else: # result is None result = None # return the result FormulaTree return result
def build_tree_helper(formula, start, end): ''' (str, int, int) -> FormulaTree/None takes string <formula> and returns the root of the FormulaTree that represents <formula>. Returns None if formula is not valid <start> is starting index of <formula> to start building tree <end> is ending index of <formula> to start building tree, is inclusive this function is recursive a valid formula contains the following: - boolean variables: lowercase alphabetical letters - connectives: '*' represents AND, '+' represent OR, '-' represent NOT - parentheses: '(' and ')' rules for formula: - the simpliest formula is just a boolean variable - more complex formulas uses connectives and variables - arguments for '*' and '+' are always enclosed by parentheses - arugments for connectives are formulas any string that cannot be constructed with rules above are not valid (i.e empty, invalid characters, using connectives without using brackets) >>> a = build_tree_helper('(x+y)', 0 ,len('(x+y)') - 1) >>> repr(a) == "OrTree(Leaf('x'), Leaf('y'))" True >>> a = build_tree_helper('(-x*(y+z))', 1, 2) >>> repr(a) == "NotTree(Leaf('x'))" True ''' # initialize result as None, so iff no valid conditions are met, ret None result = None # base case: 1 character if (start == end): # if the charcter is lowercase alphabetical letter if (formula[start].isalpha() and formula[start].islower()): # result is a Leaf node with the given letter result = Leaf(formula[start]) # base case: formula starts with '-' elif (formula[start] == '-'): # check whether rest of the formula is valid sub_result = build_tree_helper(formula, start + 1, end) # if the rest of the formula is not valid if sub_result is None: # the whole formula is not valid result = None else: # else attach the FormulaTree representing the rest of the formula # to NotTree and ret result = NotTree(sub_result) # else recursive step else: # vars to keep track of '(' and ')' left_bracket_count = 0 right_bracket_count = 0 # vars for efficient while loop exit_loop = False i = start # find the most outer brackets and the connective associated with them # iterate through forumula until they are found while (i <= end and not exit_loop): bracket_difference = left_bracket_count - right_bracket_count # keep track of how many of each bracket is in the formula if (formula[i] == '('): left_bracket_count += 1 elif (formula[i] == ')'): right_bracket_count += 1 # if there are more ')' than '(' then formula is invalid # exit loop and return None if bracket_difference < 0: exit_loop = True # connective for outer brackets is when <bracket_difference> <= 1 # and the current character is '*' or '+' elif ((formula[i] == '*' or formula[i] == '+') and bracket_difference <= 1): connective = formula[i] # divide and conquer, and build the tree to the left and right # of the connective left = build_tree_helper(formula, start + 1, i - 1) right = build_tree_helper(formula, i + 1, end - 1) # if any part of that is invalid, then whole forulma is invalid if (left is None or right is None): result = None # set the left and right tree as the children of the found # connective elif (connective == '*'): result = AndTree(left, right) elif (connective == '+'): result = OrTree(left, right) # exit the rest of the while loop, if connective is found exit_loop = True # increment the loop i += 1 # return result return result
def build_tree_helper(formula): ''' (str) -> FormulaTree Takes in a string (formula) and returns the FormulaTree that represents formula if it is a valid formula. Otherwise None is returned REQ: formula must be a valid string formula >>> asdf = build_tree("((-x+y)*-(-y+x))") >>> print(asdf) AndTree(OrTree(NotTree(Leaf('x')), Leaf('y')), NotTree(OrTree(NotTree(Leaf('y')), Leaf('x')))) >>> asdf = build_tree("((-x+y)*-(-y+x)") >>> print(asdf) None >>> asdf = build_tree("(x+y") >>> print(asdf) None >>> asdf = build_tree("x+y") >>> print(asdf) None >>> asdf = build_tree("x+y)") >>> print(asdf) None >>> asdf = build_tree("x+y*x") >>> print(asdf) None >>> asdf = build_tree("x") >>> print(asdf) Leaf('x') >>> asdf = build_tree("-y") >>> print(asdf) NotTree(Leaf('y')) >>> asdf = build_tree("(x*y)") >>> print(asdf) AndTree(Leaf('x'), Leaf('y')) >>> asdf = build_tree("X") >>> print(asdf) None >>> asdf = build_tree("x*y") >>> print(asdf) None >>> asdf = build_tree("-(x)") >>> print(asdf) None >>> asdf = build_tree("(x+(y)*z)") >>> print(asdf) None ''' # Checks if current character is a NOT operation if (formula[0] == "-"): # Adds a NotTree to the FormulaTree return NotTree(build_tree_helper(formula[1:])) # Checks if current character is a valid variable if (len(formula) == 1 and formula[0].islower()): # Adds a Leaf to the FormulaTree return Leaf(formula[0]) # Counter for keeping track of brackets counter = 0 # Keeps track of the formula(s) within the current bracket lastIndex = len(formula) - 1 # Goes through each character of the current string for i in range(1, lastIndex): # Keeps track of brackets if (formula[i] == "("): counter += 1 elif (formula[i] == ")"): counter -= 1 # If it finds the current operation elif (counter == 0): # If current operation is an OR if (formula[i] == "+"): # Adds an OrTree to the FormulaTree return OrTree(build_tree_helper(formula[1:i]), build_tree_helper(formula[i + 1:lastIndex])) # If current operation is an AND if (formula[i] == "*"): # Adds an AndTree to the FormulaTree return AndTree(build_tree_helper(formula[1:i]), build_tree_helper(formula[i + 1:lastIndex])) # For invalid formulas so a NoneType can be returned raise Exception()