summaryrefslogtreecommitdiff
path: root/trie.py
diff options
context:
space:
mode:
Diffstat (limited to 'trie.py')
-rw-r--r--trie.py293
1 files changed, 0 insertions, 293 deletions
diff --git a/trie.py b/trie.py
deleted file mode 100644
index b9a5a1a..0000000
--- a/trie.py
+++ /dev/null
@@ -1,293 +0,0 @@
-#!/usr/bin/env python3
-
-from typing import Any, Sequence
-
-
-class Trie(object):
- """
- This is a Trie class, see: https://en.wikipedia.org/wiki/Trie.
-
- It attempts to follow Pythonic container patterns. See doctests
- for examples.
-
- """
- def __init__(self):
- self.root = {}
- self.end = "~END~"
- self.length = 0
-
- def insert(self, item: Sequence[Any]):
- """
- Insert an item.
-
- >>> t = Trie()
- >>> t.insert('test')
- >>> t.__contains__('test')
- True
-
- """
- current = self.root
- for child in item:
- current = current.setdefault(child, {})
- current[self.end] = self.end
- self.length += 1
-
- def __contains__(self, item: Sequence[Any]) -> bool:
- """
- Check whether an item is in the Trie.
-
- >>> t = Trie()
- >>> t.insert('test')
- >>> t.__contains__('test')
- True
- >>> t.__contains__('testing')
- False
- >>> 'test' in t
- True
-
- """
- current = self.__traverse__(item)
- if current is None:
- return False
- else:
- return self.end in current
-
- def contains_prefix(self, item: Sequence[Any]):
- """
- Check whether a prefix is in the Trie. The prefix may or may not
- be a full item.
-
- >>> t = Trie()
- >>> t.insert('testicle')
- >>> t.contains_prefix('test')
- True
- >>> t.contains_prefix('testicle')
- True
- >>> t.contains_prefix('tessel')
- False
-
- """
- current = self.__traverse__(item)
- return current is not None
-
- def __traverse__(self, item: Sequence[Any]):
- current = self.root
- for child in item:
- if child in current:
- current = current[child]
- else:
- return None
- return current
-
- def __getitem__(self, item: Sequence[Any]):
- """Given an item, return its Trie node which contains all
- of the successor (child) node pointers. If the item is not
- a node in the Trie, raise a KeyError.
-
- >>> t = Trie()
- >>> t.insert('test')
- >>> t.insert('testicle')
- >>> t.insert('tessera')
- >>> t.insert('tesack')
- >>> t['tes']
- {'t': {'~END~': '~END~', 'i': {'c': {'l': {'e': {'~END~': '~END~'}}}}}, 's': {'e': {'r': {'a': {'~END~': '~END~'}}}}, 'a': {'c': {'k': {'~END~': '~END~'}}}}
-
- """
- ret = self.__traverse__(item)
- if ret is None:
- raise KeyError(f"Node '{item}' is not in the trie")
- return ret
-
- def delete_recursively(self, node, item: Sequence[Any]) -> bool:
- if len(item) == 1:
- del node[item]
- if len(node) == 0 and node is not self.root:
- del node
- return True
- else:
- return False
- else:
- car = item[0]
- cdr = item[1:]
- lower = node[car]
- if self.delete_recursively(lower, cdr):
- return self.delete_recursively(node, car)
- return False
-
- def __delitem__(self, item: Sequence[Any]):
- """
- Delete an item from the Trie.
-
- >>> t = Trie()
- >>> t.insert('test')
- >>> t.insert('tess')
- >>> t.insert('tessel')
- >>> len(t)
- 3
- >>> t.root
- {'t': {'e': {'s': {'t': {'~END~': '~END~'}, 's': {'~END~': '~END~', 'e': {'l': {'~END~': '~END~'}}}}}}}
- >>> t.__delitem__('test')
- >>> len(t)
- 2
- >>> t.root
- {'t': {'e': {'s': {'s': {'~END~': '~END~', 'e': {'l': {'~END~': '~END~'}}}}}}}
- >>> for x in t:
- ... print(x)
- tess
- tessel
- >>> t.__delitem__('tessel')
- >>> len(t)
- 1
- >>> t.root
- {'t': {'e': {'s': {'s': {'~END~': '~END~'}}}}}
- >>> for x in t:
- ... print(x)
- tess
- >>> t.__delitem__('tess')
- >>> len(t)
- 0
- >>> t.root
- {}
- >>> t.insert('testy')
- >>> len(t)
- 1
-
- """
- if item not in self:
- raise KeyError(f"Node '{item}' is not in the trie")
- self.delete_recursively(self.root, item)
- self.length -= 1
-
- def __len__(self):
- """
- Returns a count of the Trie's item population.
-
- >>> t = Trie()
- >>> len(t)
- 0
- >>> t.insert('test')
- >>> len(t)
- 1
- >>> t.insert('testicle')
- >>> len(t)
- 2
-
- """
- return self.length
-
- def __iter__(self):
- self.content_generator = self.generate_recursively(self.root, '')
- return self
-
- def generate_recursively(self, node, path: Sequence[Any]):
- """
- Generate items in the trie one by one.
-
- >>> t = Trie()
- >>> t.insert('test')
- >>> t.insert('tickle')
- >>> for item in t.generate_recursively(t.root, ''):
- ... print(item)
- test
- tickle
-
- """
- for child in node:
- if child == self.end:
- yield path
- else:
- yield from self.generate_recursively(node[child], path + child)
-
- def __next__(self):
- """
- Iterate through the contents of the trie.
-
- >>> t = Trie()
- >>> t.insert('test')
- >>> t.insert('tickle')
- >>> for item in t:
- ... print(item)
- test
- tickle
-
- """
- ret = next(self.content_generator)
- if ret is not None:
- return ret
- raise StopIteration
-
- def successors(self, item: Sequence[Any]):
- """
- Return a list of the successors of an item.
-
- >>> t = Trie()
- >>> t.insert('what')
- >>> t.insert('who')
- >>> t.insert('when')
- >>> t.successors('wh')
- ['a', 'o', 'e']
-
- >>> u = Trie()
- >>> u.insert(['this', 'is', 'a', 'test'])
- >>> u.insert(['this', 'is', 'a', 'robbery'])
- >>> u.insert(['this', 'is', 'a', 'walrus'])
- >>> u.successors(['this', 'is', 'a'])
- ['test', 'robbery', 'walrus']
-
- """
- node = self.__traverse__(item)
- if node is None:
- return None
- return [x for x in node if x != self.end]
-
- def repr_recursive(self, node, delimiter):
- """
- A friendly string representation of the contents of the Trie.
-
- >>> t = Trie()
- >>> t.insert([10, 0, 0, 1])
- >>> t.insert([10, 0, 0, 2])
- >>> t.insert([10, 10, 10, 1])
- >>> t.insert([10, 10, 10, 2])
- >>> t.repr_recursive(t.root, '.')
- '10.[0.0.[1, 2], 10.10.[1, 2]]'
- >>> print(t)
- 10[00[1, 2], 1010[1, 2]]
-
- """
- child_count = 0
- my_rep = ''
- for child in node:
- if child != self.end:
- child_count += 1
- child_rep = self.repr_recursive(node[child], delimiter)
- if len(child_rep) > 0:
- my_rep += str(child) + delimiter + child_rep + ", "
- else:
- my_rep += str(child) + ", "
- if len(my_rep) > 1:
- my_rep = my_rep[:-2]
- if child_count > 1:
- my_rep = f'[{my_rep}]'
- return my_rep
-
- def __repr__(self):
- """
- A friendly string representation of the contents of the Trie. Under
- the covers uses repr_recursive with no delimiter
-
- >>> t = Trie()
- >>> t.insert([10, 0, 0, 1])
- >>> t.insert([10, 0, 0, 2])
- >>> t.insert([10, 10, 10, 1])
- >>> t.insert([10, 10, 10, 2])
- >>> print(t)
- 10[00[1, 2], 1010[1, 2]]
-
- """
- return self.repr_recursive(self.root, '')
-
-
-if __name__ == '__main__':
- import doctest
- doctest.testmod()