"""The itree module contains functions implementing the immutable tree (ITree) data abstractions for CS61A at UC Berkeley. ITrees are (immutable) trees, a common class of data structures in computer science which organizes data into a hierarchical structure. One might notice that none of the doctests for either ADT actually "show" an ITree. This is because this ADT is meant to be treated as a black box: the tests do not assume any specific implementation for ITrees. """ # General Immutable Trees Abstraction def make_itree(datum, children=()): """Create an ITree with datum and children. >>> print(itree_str(make_itree(1))) 1 >>> print(itree_str(make_itree(1, ... (make_itree(2),)))) 1 | +-2 >>> print(itree_str(make_itree(1, ... (make_itree(2, ... (make_itree(3), ... make_itree(4))), ... make_itree(5, ... (make_itree(6), ... make_itree(7))))))) 1 | +-2 | | | +-3 | | | +-4 | +-5 | +-6 | +-7 """ return (datum, children) def itree_datum(t): """Return the datum of ITree t. >>> test_tree = make_itree(4, (make_itree(11), ... make_itree(23))) >>> itree_datum(test_tree) 4 """ return t[0] def itree_children(t): """Return the children of ITree t. >>> test_tree = make_itree(4, (make_itree(11), ... make_itree(23))) >>> itree_datum(itree_children(test_tree)[0]) 11 """ return t[1] # General Immutable Trees Utility Functions def itree_items(t): """Returns a tuple of all items in the ITree t. >>> test_tree = make_itree(1, ... (make_itree(2, ... (make_itree(3), ... make_itree(4))), ... make_itree(5, ... (make_itree(6), ... make_itree(7))))) >>> print(itree_str(test_tree)) 1 | +-2 | | | +-3 | | | +-4 | +-5 | +-6 | +-7 >>> itree_items(test_tree) (1, 2, 3, 4, 5, 6, 7) """ from functools import reduce from operator import add return (itree_datum(t),) + \ reduce(add, map(itree_items, itree_children(t)), ()) def itree_size(t): """Returns the number of items in an ITree t. >>> test_tree = make_itree(1, ... (make_itree(2, ... (make_itree(3), ... make_itree(4))), ... make_itree(5, ... (make_itree(6), ... make_itree(7))))) >>> itree_size(test_tree) 7 """ return len(itree_items(t)) def is_leaf(t): """Returns True if the ITree t is a leaf (or has no children). >>> is_leaf(make_itree(2)) True >>> is_leaf(make_itree(1, (make_itree(2), make_itree(3)))) False """ return len(itree_children(t)) == 0 def itree_map(func, t): """Returns a new ITree with the results of applying func to each of the items in t. >>> test_tree = make_itree(1, ... (make_itree(2, ... (make_itree(3), ... make_itree(4))), ... make_itree(5, ... (make_itree(6), ... make_itree(7))))) >>> print(itree_str(test_tree)) 1 | +-2 | | | +-3 | | | +-4 | +-5 | +-6 | +-7 >>> print(itree_str(itree_map(lambda x: x * x, test_tree))) 1 | +-4 | | | +-9 | | | +-16 | +-25 | +-36 | +-49 """ return make_itree(func(itree_datum(t)), tuple(map(lambda child: itree_map(func, child), itree_children(t)))) def itree_str(t, depth=0, more_children=False): """Returns a string representation of the ITree t. Arguments: t -- the tree to be printed depth -- the depth (in case it's a subtree) more_children -- do we need to put a character at the front of each line to connect more children after this tree? >>> print(itree_str(make_itree(1, ... (make_itree(2), ... make_itree(3, (make_itree(4),)), ... make_itree(5, (make_itree(6), ... make_itree(7))))))) 1 | +-2 | +-3 | | | +-4 | +-5 | +-6 | +-7 >>> print(itree_str(make_itree(1, ... (make_itree(2, ... (make_itree(3, ... (make_itree(4), ... make_itree(5))), ... make_itree(6, ... (make_itree(7), ... make_itree(8))))), ... make_itree(9, ... (make_itree(10, ... (make_itree(11), ... make_itree(12))), ... make_itree(13, ... (make_itree(14), ... make_itree(15))))))))) 1 | +-2 | | | +-3 | | | | | +-4 | | | | | +-5 | | | +-6 | | | +-7 | | | +-8 | +-9 | +-10 | | | +-11 | | | +-12 | +-13 | +-14 | +-15 """ result = repr(itree_datum(t)) last_child = len(itree_children(t)) - 1 for child_n in range(len(itree_children(t))): result += "\n|\n+-" result += itree_str(itree_children(t)[child_n], depth + 1, child_n != last_child) # Add front padding if we're part of a bigger tree we're converting to a # string. Perhaps not the most elegant code. if depth > 0: if more_children: leader = "| " else: leader = " " result = result.replace("\n", "\n" + leader) return result