-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
implement zipper #783
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
implement zipper #783
Changes from 5 commits
37386e9
deb6fea
ade6c1f
b10b518
7aad5f1
98d4066
f23aee0
7cd3c4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Zipper | ||
|
||
Creating a zipper for a binary tree. | ||
|
||
[Zippers](https://en.wikipedia.org/wiki/Zipper_%28data_structure%29) are | ||
a purely functional way of navigating within a data structure and | ||
manipulating it. They essentially contain a data structure and a | ||
pointer into that data structure (called the focus). | ||
|
||
For example given a rose tree (where each node contains a value and a | ||
list of child nodes) a zipper might support these operations: | ||
|
||
- `from_tree` (get a zipper out of a rose tree, the focus is on the root node) | ||
- `to_tree` (get the rose tree out of the zipper) | ||
- `value` (get the value of the focus node) | ||
- `prev` (move the focus to the previous child of the same parent, | ||
returns a new zipper) | ||
- `next` (move the focus to the next child of the same parent, returns a | ||
new zipper) | ||
- `up` (move the focus to the parent, returns a new zipper) | ||
- `set_value` (set the value of the focus node, returns a new zipper) | ||
- `insert_before` (insert a new subtree before the focus node, it | ||
becomes the `prev` of the focus node, returns a new zipper) | ||
- `insert_after` (insert a new subtree after the focus node, it becomes | ||
the `next` of the focus node, returns a new zipper) | ||
- `delete` (removes the focus node and all subtrees, focus moves to the | ||
`next` node if possible otherwise to the `prev` node if possible, | ||
otherwise to the parent node, returns a new zipper) | ||
|
||
### Submitting Exercises | ||
|
||
Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory. | ||
|
||
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`. | ||
|
||
|
||
For more detailed information about running tests, code style and linting, | ||
please see the [help page](http://exercism.io/languages/python). | ||
|
||
|
||
## Submitting Incomplete Solutions | ||
|
||
It's possible to submit an incomplete solution so you can see how others have completed the exercise. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
class Zipper(object): | ||
@staticmethod | ||
def from_tree(tree): | ||
return Zipper(dict(tree), []) | ||
|
||
def __init__(self, tree, ancestors): | ||
self.tree = tree | ||
self.ancestors = ancestors | ||
|
||
def value(self): | ||
return self.tree['value'] | ||
|
||
def set_value(self, value): | ||
self.tree['value'] = value | ||
return self | ||
|
||
def left(self): | ||
if self.tree['left'] is None: | ||
return None | ||
return Zipper(self.tree['left'], self.ancestors + [self.tree]) | ||
|
||
def set_left(self, tree): | ||
self.tree['left'] = tree | ||
return self | ||
|
||
def right(self): | ||
if self.tree['right'] is None: | ||
return None | ||
return Zipper(self.tree['right'], self.ancestors + [self.tree]) | ||
|
||
def set_right(self, tree): | ||
self.tree['right'] = tree | ||
return self | ||
|
||
def up(self): | ||
return Zipper(self.ancestors[-1], self.ancestors[:-1]) | ||
|
||
def to_tree(self): | ||
if any(self.ancestors): | ||
return self.ancestors[0] | ||
return self.tree |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
class Zipper(object): | ||
@staticmethod | ||
def from_tree(tree): | ||
return Zipper(tree) | ||
|
||
def __init__(self, root, parent=None): | ||
self.root = dict(root) | ||
self.parent = None if parent is None else dict(parent) | ||
|
||
def value(self): | ||
return self.root['value'] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should |
||
|
||
def set_value(self): | ||
pass | ||
|
||
def left(self): | ||
pass | ||
|
||
def set_left(self): | ||
pass | ||
|
||
def right(self): | ||
pass | ||
|
||
def set_right(self): | ||
pass | ||
|
||
def up(self): | ||
pass | ||
|
||
def to_tree(self): | ||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import unittest | ||
|
||
from zipper import Zipper | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please also leave a comment stating what version of
|
||
def bt(value, left, right): | ||
return { | ||
'value': value, | ||
'left': left, | ||
'right': right | ||
} | ||
|
||
|
||
def leaf(value): | ||
return bt(value, None, None) | ||
|
||
|
||
EMPTY_TREE = None | ||
|
||
|
||
def create_trees(): | ||
t1 = bt(1, bt(2, EMPTY_TREE, leaf(3)), leaf(4)) | ||
t2 = bt(1, bt(5, EMPTY_TREE, leaf(3)), leaf(4)) | ||
t3 = bt(1, bt(2, leaf(5), leaf(3)), leaf(4)) | ||
t4 = bt(1, leaf(2), leaf(4)) | ||
return (t1, t2, t3, t4) | ||
|
||
|
||
class ZipperTest(unittest.TestCase): | ||
def test_data_is_retained(self): | ||
t1, _, _, _ = create_trees() | ||
zipper = Zipper.from_tree(t1) | ||
tree = zipper.to_tree() | ||
self.assertEqual(tree, t1) | ||
|
||
def test_left_and_right_value(self): | ||
t1, _, _, _ = create_trees() | ||
zipper = Zipper.from_tree(t1) | ||
self.assertEqual(zipper.left().right().value(), 3) | ||
|
||
def test_dead_end(self): | ||
t1, _, _, _ = create_trees() | ||
zipper = Zipper.from_tree(t1) | ||
self.assertIsNone(zipper.left().left()) | ||
|
||
def test_tree_from_deep_focus(self): | ||
t1, _, _, _ = create_trees() | ||
zipper = Zipper.from_tree(t1) | ||
self.assertEqual(zipper.left().right().to_tree(), t1) | ||
|
||
def test_set_value(self): | ||
t1, t2, _, _ = create_trees() | ||
zipper = Zipper.from_tree(t1) | ||
updatedZipper = zipper.left().set_value(5) | ||
tree = updatedZipper.to_tree() | ||
self.assertEqual(tree, t2) | ||
|
||
def test_set_left_with_value(self): | ||
t1, _, t3, _ = create_trees() | ||
zipper = Zipper.from_tree(t1) | ||
updatedZipper = zipper.left().set_left(leaf(5)) | ||
tree = updatedZipper.to_tree() | ||
self.assertEqual(tree, t3) | ||
|
||
def test_set_right_to_none(self): | ||
t1, _, _, t4 = create_trees() | ||
zipper = Zipper.from_tree(t1) | ||
updatedZipper = zipper.left().set_right(None) | ||
tree = updatedZipper.to_tree() | ||
self.assertEqual(tree, t4) | ||
|
||
def test_different_paths_to_same_zipper(self): | ||
t1, _, _, _ = create_trees() | ||
zipper = Zipper.from_tree(t1) | ||
self.assertEqual(zipper.left().up().right().to_tree(), | ||
zipper.right().to_tree()) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pass