Advertisement
isarafx

yo

Jan 20th, 2019
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.50 KB | None | 0 0
  1. class Tree:
  2. """Abstract base class representing a tree structure."""
  3.  
  4. # ------------------------------- nested Position class -------------------------------
  5. class Position:
  6. """An abstraction representing the location of a single element within a tree.
  7.  
  8. Note that two position instaces may represent the same inherent location in a tree.
  9. Therefore, users should always rely on syntax 'p == q' rather than 'p is q' when testing
  10. equivalence of positions.
  11. """
  12.  
  13. def element(self):
  14. """Return the element stored at this Position."""
  15. raise NotImplementedError('must be implemented by subclass')
  16.  
  17. def __eq__(self, other):
  18. """Return True if other Position represents the same location."""
  19. raise NotImplementedError('must be implemented by subclass')
  20.  
  21. def __ne__(self, other):
  22. """Return True if other does not represent the same location."""
  23. return not (self == other) # opposite of __eq__
  24.  
  25. # ---------- abstract methods that concrete subclass must support ----------
  26. def root(self):
  27. """Return Position representing the tree's root (or None if empty)."""
  28. raise NotImplementedError('must be implemented by subclass')
  29.  
  30. def parent(self, p):
  31. """Return Position representing p's parent (or None if p is root)."""
  32. raise NotImplementedError('must be implemented by subclass')
  33.  
  34. def num_children(self, p):
  35. """Return the number of children that Position p has."""
  36. raise NotImplementedError('must be implemented by subclass')
  37.  
  38. def children(self, p):
  39. """Generate an iteration of Positions representing p's children."""
  40. raise NotImplementedError('must be implemented by subclass')
  41.  
  42. def __len__(self):
  43. """Return the total number of elements in the tree."""
  44. raise NotImplementedError('must be implemented by subclass')
  45.  
  46. # ---------- concrete methods implemented in this class ----------
  47. def is_root(self, p):
  48. """Return True if Position p represents the root of the tree."""
  49. return self.root() == p
  50.  
  51. def is_leaf(self, p):
  52. """Return True if Position p does not have any children."""
  53. return self.num_children(p) == 0
  54.  
  55. def is_empty(self):
  56. """Return True if the tree is empty."""
  57. return len(self) == 0
  58.  
  59. def depth(self, p):
  60. """Return the number of levels separating Position p from the root."""
  61. if self.is_root(p):
  62. return 0
  63. else:
  64. return 1 + self.depth(self.parent(p))
  65.  
  66. def _height1(self): # works, but O(n^2) worst-case time
  67. """Return the height of the tree."""
  68. return max(self.depth(p) for p in self.positions() if self.is_leaf(p))
  69.  
  70. def _height2(self, p): # time is linear in size of subtree
  71. """Return the height of the subtree rooted at Position p."""
  72. if self.is_leaf(p):
  73. return 0
  74. else:
  75. return 1 + max(self._height2(c) for c in self.children(p))
  76.  
  77. def height(self, p=None):
  78. """Return the height of the subtree rooted at Position p.
  79.  
  80. If p is None, return the height of the entire tree.
  81. """
  82. if p is None:
  83. p = self.root()
  84. return self._height2(p) # start _height2 recursion
  85.  
  86. def __iter__(self):
  87. """Generate an iteration of the tree's elements."""
  88. for p in self.positions(): # use same order as positions()
  89. yield p.element() # but yield each element
  90.  
  91. def positions(self):
  92. """Generate an iteration of the tree's positions."""
  93. return self.preorder() # return entire preorder iteration
  94.  
  95. def preorder(self):
  96. """Generate a preorder iteration of positions in the tree."""
  97. if not self.is_empty():
  98. for p in self._subtree_preorder(self.root()): # start recursion
  99. yield p
  100.  
  101. def _subtree_preorder(self, p):
  102. """Generate a preorder iteration of positions in subtree rooted at p."""
  103. yield p # visit p before its subtrees
  104. for c in self.children(p): # for each child c
  105. for other in self._subtree_preorder(c): # do preorder of c's subtree
  106. yield other # yielding each to our caller
  107.  
  108. def postorder(self):
  109. """Generate a postorder iteration of positions in the tree."""
  110. if not self.is_empty():
  111. for p in self._subtree_postorder(self.root()): # start recursion
  112. yield p
  113.  
  114. def _subtree_postorder(self, p):
  115. """Generate a postorder iteration of positions in subtree rooted at p."""
  116. for c in self.children(p): # for each child c
  117. for other in self._subtree_postorder(c): # do postorder of c's subtree
  118. yield other # yielding each to our caller
  119. yield p # visit p after its subtrees
  120.  
  121. def breadthfirst(self):
  122. """Generate a breadth-first iteration of the positions of the tree."""
  123. if not self.is_empty():
  124. fringe = LinkedQueue() # known positions not yet yielded
  125. fringe.enqueue(self.root()) # starting with the root
  126. while not fringe.is_empty():
  127. p = fringe.dequeue() # remove from front of the queue
  128. yield p # report this position
  129. for c in self.children(p):
  130. fringe.enqueue(c) # add children to back of queue
  131. ###########################################################################
  132. class BinaryTree(Tree):
  133. """Abstract base class representing a binary tree structure."""
  134.  
  135. # --------------------- additional abstract methods ---------------------
  136. def left(self, p):
  137. """Return a Position representing p's left child.
  138.  
  139. Return None if p does not have a left child.
  140. """
  141. raise NotImplementedError('must be implemented by subclass')
  142.  
  143. def right(self, p):
  144. """Return a Position representing p's right child.
  145.  
  146. Return None if p does not have a right child.
  147. """
  148. raise NotImplementedError('must be implemented by subclass')
  149.  
  150. # ---------- concrete methods implemented in this class ----------
  151. def sibling(self, p):
  152. """Return a Position representing p's sibling (or None if no sibling)."""
  153. parent = self.parent(p)
  154. if parent is None: # p must be the root
  155. return None # root has no sibling
  156. else:
  157. if p == self.left(parent):
  158. return self.right(parent) # possibly None
  159. else:
  160. return self.left(parent) # possibly None
  161.  
  162. def children(self, p):
  163. """Generate an iteration of Positions representing p's children."""
  164. if self.left(p) is not None:
  165. yield self.left(p)
  166. if self.right(p) is not None:
  167. yield self.right(p)
  168.  
  169. def inorder(self):
  170. """Generate an inorder iteration of positions in the tree."""
  171. if not self.is_empty():
  172. for p in self._subtree_inorder(self.root()):
  173. yield p
  174.  
  175. def _subtree_inorder(self, p):
  176. """Generate an inorder iteration of positions in subtree rooted at p."""
  177. if self.left(p) is not None: # if left child exists, traverse its subtree
  178. for other in self._subtree_inorder(self.left(p)):
  179. yield other
  180. yield p # visit p between its subtrees
  181. if self.right(p) is not None: # if right child exists, traverse its subtree
  182. for other in self._subtree_inorder(self.right(p)):
  183. yield other
  184.  
  185. # override inherited version to make inorder the default
  186. def positions(self):
  187. """Generate an iteration of the tree's positions."""
  188. return self.inorder() # make inorder the default
  189. #########################################################################
  190. class LinkedBinaryTree(BinaryTree):
  191. """Linked representation of a binary tree structure."""
  192.  
  193. # -------------------------- nested _Node class --------------------------
  194. class _Node:
  195. """Lightweight, nonpublic class for storing a node."""
  196. __slots__ = '_element', '_parent', '_left', '_right' # streamline memory usage
  197.  
  198. def __init__(self, element, parent=None, left=None, right=None):
  199. self._element = element
  200. self._parent = parent
  201. self._left = left
  202. self._right = right
  203.  
  204. # -------------------------- nested Position class --------------------------
  205. class Position(BinaryTree.Position):
  206. """An abstraction representing the location of a single element."""
  207.  
  208. def __init__(self, container, node):
  209. """Constructor should not be invoked by user."""
  210. self._container = container
  211. self._node = node
  212.  
  213. def element(self):
  214. """Return the element stored at this Position."""
  215. return self._node._element
  216.  
  217. def __eq__(self, other):
  218. """Return True if other is a Position representing the same location."""
  219. return type(other) is type(self) and other._node is self._node
  220.  
  221. # ------------------------------- utility methods -------------------------------
  222. def _validate(self, p):
  223. """Return associated node, if position is valid."""
  224. if not isinstance(p, self.Position):
  225. raise TypeError('p must be proper Position type')
  226. if p._container is not self:
  227. raise ValueError('p does not belong to this container')
  228. if p._node._parent is p._node: # convention for deprecated nodes
  229. raise ValueError('p is no longer valid')
  230. return p._node
  231.  
  232. def _make_position(self, node):
  233. """Return Position instance for given node (or None if no node)."""
  234. return self.Position(self, node) if node is not None else None
  235.  
  236. # -------------------------- binary tree constructor --------------------------
  237. def __init__(self):
  238. """Create an initially empty binary tree."""
  239. self._root = None
  240. self._size = 0
  241.  
  242. # -------------------------- public accessors --------------------------
  243. def __len__(self):
  244. """Return the total number of elements in the tree."""
  245. return self._size
  246.  
  247. def root(self):
  248. """Return the root Position of the tree (or None if tree is empty)."""
  249. return self._make_position(self._root)
  250.  
  251. def parent(self, p):
  252. """Return the Position of p's parent (or None if p is root)."""
  253. node = self._validate(p)
  254. return self._make_position(node._parent)
  255.  
  256. def left(self, p):
  257. """Return the Position of p's left child (or None if no left child)."""
  258. node = self._validate(p)
  259. return self._make_position(node._left)
  260.  
  261. def right(self, p):
  262. """Return the Position of p's right child (or None if no right child)."""
  263. node = self._validate(p)
  264. return self._make_position(node._right)
  265.  
  266. def num_children(self, p):
  267. """Return the number of children of Position p."""
  268. node = self._validate(p)
  269. count = 0
  270. if node._left is not None: # left child exists
  271. count += 1
  272. if node._right is not None: # right child exists
  273. count += 1
  274. return count
  275.  
  276. # -------------------------- nonpublic mutators --------------------------
  277. def _add_root(self, e):
  278. """Place element e at the root of an empty tree and return new Position.
  279.  
  280. Raise ValueError if tree nonempty.
  281. """
  282. if self._root is not None:
  283. raise ValueError('Root exists')
  284. self._size = 1
  285. self._root = self._Node(e)
  286. return self._make_position(self._root)
  287.  
  288. def _add_left(self, p, e):
  289. """Create a new left child for Position p, storing element e.
  290.  
  291. Return the Position of new node.
  292. Raise ValueError if Position p is invalid or p already has a left child.
  293. """
  294. node = self._validate(p)
  295. if node._left is not None:
  296. raise ValueError('Left child exists')
  297. self._size += 1
  298. node._left = self._Node(e, node) # node is its parent
  299. return self._make_position(node._left)
  300.  
  301. def _add_right(self, p, e):
  302. """Create a new right child for Position p, storing element e.
  303.  
  304. Return the Position of new node.
  305. Raise ValueError if Position p is invalid or p already has a right child.
  306. """
  307. node = self._validate(p)
  308. if node._right is not None:
  309. raise ValueError('Right child exists')
  310. self._size += 1
  311. node._right = self._Node(e, node) # node is its parent
  312. return self._make_position(node._right)
  313.  
  314. def _replace(self, p, e):
  315. """Replace the element at position p with e, and return old element."""
  316. node = self._validate(p)
  317. old = node._element
  318. node._element = e
  319. return old
  320.  
  321. def _delete(self, p):
  322. """Delete the node at Position p, and replace it with its child, if any.
  323.  
  324. Return the element that had been stored at Position p.
  325. Raise ValueError if Position p is invalid or p has two children.
  326. """
  327. node = self._validate(p)
  328. if self.num_children(p) == 2:
  329. raise ValueError('Position has two children')
  330. child = node._left if node._left else node._right # might be None
  331. if child is not None:
  332. child._parent = node._parent # child's grandparent becomes parent
  333. if node is self._root:
  334. self._root = child # child becomes root
  335. else:
  336. parent = node._parent
  337. if node is parent._left:
  338. parent._left = child
  339. else:
  340. parent._right = child
  341. self._size -= 1
  342. node._parent = node # convention for deprecated node
  343. return node._element
  344.  
  345. def _attach(self, p, t1, t2):
  346. """Attach trees t1 and t2, respectively, as the left and right subtrees of the external Position p.
  347.  
  348. As a side effect, set t1 and t2 to empty.
  349. Raise TypeError if trees t1 and t2 do not match type of this tree.
  350. Raise ValueError if Position p is invalid or not external.
  351. """
  352. node = self._validate(p)
  353. if not self.is_leaf(p):
  354. raise ValueError('position must be leaf')
  355. if not type(self) is type(t1) is type(t2): # all 3 trees must be same type
  356. raise TypeError('Tree types must match')
  357. self._size += len(t1) + len(t2)
  358. if not t1.is_empty(): # attached t1 as left subtree of node
  359. t1._root._parent = node
  360. node._left = t1._root
  361. t1._root = None # set t1 instance to empty
  362. t1._size = 0
  363. if not t2.is_empty(): # attached t2 as right subtree of node
  364. t2._root._parent = node
  365. node._right = t2._root
  366. t2._root = None # set t2 instance to empty
  367. t2._size = 0
  368. class ExpressionTree(LinkedBinaryTree):
  369. """An arithmetic expression tree."""
  370.  
  371. def __init__(self, token, left=None, right=None):
  372. """Create an expression tree.
  373.  
  374. In a single parameter form, token should be a leaf value (e.g., '42'),
  375. and the expression tree will have that value at an isolated node.
  376.  
  377. In a three-parameter version, token should be an operator,
  378. and left and right should be existing ExpressionTree instances
  379. that become the operands for the binary operator.
  380. """
  381. super().__init__() # LinkedBinaryTree initialization
  382. if not isinstance(token, str):
  383. raise TypeError('Token must be a string')
  384. self._add_root(token) # use inherited, nonpublic method
  385. if left is not None: # presumably three-parameter form
  386. if token not in '+-*x/^':
  387. raise ValueError('token must be valid operator')
  388. self._attach(self.root(), left, right) # use inherited, nonpublic method
  389.  
  390. def __str__(self):
  391. """Return string representation of the expression."""
  392. pieces = [] # sequence of piecewise strings to compose
  393. self._parenthesize_recur(self.root(), pieces)
  394. print(pieces)
  395. return ''.join(pieces)
  396.  
  397. def _parenthesize_recur(self, p, result):
  398. """Append piecewise representation of p's subtree to resulting list."""
  399. if self.is_leaf(p):
  400. result.append(str(p.element())) # leaf value as a string
  401. else:
  402. result.append('(') # opening parenthesis
  403. self._parenthesize_recur(self.left(p), result) # left subtree
  404. result.append(p.element()) # operator
  405. self._parenthesize_recur(self.right(p), result) # right subtree
  406. result.append(')') # closing parenthesis
  407.  
  408. def evaluate(self):
  409. """Return the numeric result of the expression."""
  410. return self._evaluate_recur(self.root())
  411.  
  412. def _evaluate_recur(self, p):
  413. """Return the numeric result of subtree rooted at p."""
  414. if self.is_leaf(p):
  415. return float(p.element()) # we assume element is numeric
  416. else:
  417. op = p.element()
  418. left_val = self._evaluate_recur(self.left(p))
  419. right_val = self._evaluate_recur(self.right(p))
  420. if op == '+':
  421. return left_val + right_val
  422. elif op == '-':
  423. return left_val - right_val
  424. elif op == '/':
  425. return left_val / right_val
  426. elif op == '^':
  427. return left_val ** right_val
  428. else: # treat 'x' or '*' as multiplication
  429. return left_val * right_val
  430. #######################################################################
  431. def tokenize(raw):
  432. """Produces list of tokens indicated by a raw expression string.
  433.  
  434. For example the string '(43-(3*10))' results in the list
  435. ['(', '43', '-', '(', '3', '*', '10', ')', ')']
  436. """
  437. SYMBOLS = set('+-x*/() ') # allow for '*' or 'x' for multiplication
  438.  
  439. mark = 0
  440. tokens = []
  441. n = len(raw)
  442. for j in range(n):
  443. if raw[j] in SYMBOLS:
  444. if mark != j:
  445. tokens.append(raw[mark:j]) # complete preceding token
  446. if raw[j] != ' ':
  447. tokens.append(raw[j]) # include this token
  448. mark = j+1 # update mark to being at next index
  449. if mark != n:
  450. tokens.append(raw[mark:n]) # complete preceding token
  451. return tokens
  452. def build_expression_tree(tokens):
  453. """Returns an ExpressionTree based upon by a tokenized expression.
  454.  
  455. tokens must be an iterable of strings representing a fully parenthesized
  456. binary expression, such as ['(', '43', '-', '(', '3', '*', '10', ')', ')']
  457. """
  458. S = [] # we use Python list as stack
  459. for t in tokens:
  460. if t in '+-x*/': # t is an operator symbol
  461. S.append(t) # push the operator symbol
  462. elif t not in '()': # consider t to be a literal
  463. S.append(ExpressionTree(t)) # push trivial tree storing value
  464. elif t == ')': # compose a new tree from three constituent parts
  465. right = S.pop() # right subtree as per LIFO
  466. op = S.pop() # operator symbol
  467. left = S.pop() # left subtree
  468. S.append(ExpressionTree(op, left, right)) # repush tree
  469. # we ignore a left parenthesis
  470. return S.pop()
  471.  
  472.  
  473.  
  474.  
  475. if __name__ == '__main__':
  476. print(tokenize('((2^2/(7-(1+1)))*3)-(2+(1+1))'))
  477. big = build_expression_tree('((2^2/(7-(1+1)))*3)-(2+(1+1))')
  478. print(big, '=', big.evaluate())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement