瀏覽代碼

Implement new exercise: Zipper (#1327)

* Implement Zipper

* Update config

* Update readme

* Minor changes

* Update all methods to package private
Lu Lechuan 8 年之前
父節點
當前提交
938fc0d9b0

+ 13
- 0
config.json 查看文件

1052
     },
1052
     },
1053
     {
1053
     {
1054
       "core": false,
1054
       "core": false,
1055
+      "difficulty": 7,
1056
+      "slug": "zipper",
1057
+      "topics": [
1058
+        "integer",
1059
+        "nodes",
1060
+        "tree",
1061
+        "links"
1062
+      ],
1063
+      "unlocked_by": "linked-list",
1064
+      "uuid": "25d2c7a5-1ec8-464a-b3de-ad1b27464ef1"
1065
+    },
1066
+    {
1067
+      "core": false,
1055
       "difficulty": 8,
1068
       "difficulty": 8,
1056
       "slug": "ocr-numbers",
1069
       "slug": "ocr-numbers",
1057
       "topics": [
1070
       "topics": [

+ 1
- 0
exercises/settings.gradle 查看文件

95
 include 'word-count'
95
 include 'word-count'
96
 include 'word-search'
96
 include 'word-search'
97
 include 'wordy'
97
 include 'wordy'
98
+include 'zipper'

+ 199
- 0
exercises/zipper/.meta/src/reference/java/Zipper.java 查看文件

1
+class Zipper {
2
+
3
+    private int value;
4
+    BinaryTree tree; // Only the root node will bind to the BinaryTree
5
+    Zipper up;
6
+    Zipper left;
7
+    Zipper right;
8
+
9
+    Zipper(int val) {
10
+        value = val;
11
+        tree = null;
12
+        up = null;
13
+        left = null;
14
+        right = null;
15
+    }
16
+
17
+    /**
18
+     * Get a zipper out of a rose tree, the focus is on the root node
19
+     */
20
+    Zipper fromTree() {
21
+        Zipper current = this;
22
+        while (current.up != null) {
23
+            current = current.up;
24
+        }
25
+        return current;
26
+    }
27
+
28
+    BinaryTree toTree() {
29
+        Zipper current = this;
30
+        while (current.up != null) {
31
+            current = current.up;
32
+        }
33
+        return current.tree;
34
+    }
35
+
36
+    int getValue() {
37
+        return value;
38
+    }
39
+
40
+    Zipper setLeft(Zipper leftChild) {
41
+        left = leftChild;
42
+        if (leftChild != null) {
43
+            leftChild.up = this;
44
+            leftChild.tree = null;
45
+        }
46
+        return this;
47
+    }
48
+
49
+    Zipper setRight(Zipper rightChild) {
50
+        right = rightChild;
51
+        if (rightChild != null) {
52
+            rightChild.up = this;
53
+            rightChild.tree = null;
54
+        }
55
+        return this;
56
+    }
57
+
58
+    /**
59
+     * Move the focus to the previous child of the same parent
60
+     * @return a new Zipper
61
+     * Conditions: 1. The current Zipper must have a parent, i.e. it is not the root node.
62
+     * 2. The current Zipper must be the right child of its parent
63
+     */
64
+    Zipper getPrev() {
65
+        Zipper current = this;
66
+        try {
67
+            current = this.up;
68
+            current = current.left;
69
+            return current;
70
+        } catch (Exception e) {
71
+            System.out.println(e);
72
+        }
73
+        return null;
74
+    }
75
+
76
+    /**
77
+     * Move the focus to the next child of the same parent
78
+     * @return a new Zipper
79
+     * Conditions: 1. The current Zipper must have a parent, i.e. it is not the root node.
80
+     * 2. The current Zipper must be the left child of its parent
81
+     */
82
+    Zipper getNext() {
83
+        Zipper current = this;
84
+        try {
85
+            current = this.up;
86
+            current = current.right;
87
+            return current;
88
+        } catch (Exception e) {
89
+            System.out.println(e);
90
+        }
91
+        return null;
92
+    }
93
+
94
+    /**
95
+     * Move the focus to the parent
96
+     * @return a new Zipper
97
+     */
98
+    Zipper getUp() {
99
+        return up;
100
+    }
101
+
102
+    /**
103
+     * Set the value of the focus node, returns a new zipper
104
+     * @param val
105
+     */
106
+    void setValue(int val) {
107
+        this.value = val;
108
+    }
109
+
110
+    /**
111
+     * Insert a new subtree before the focus node, it becomes the prev of the focus node
112
+     * @return a new Zipper
113
+     */
114
+    Zipper insertBefore(Zipper toTheLeft) {
115
+        this.left = toTheLeft;
116
+        return this.left;
117
+    }
118
+
119
+    /**
120
+     * Insert a new subtree after the focus node, it becomes the next of the focus node
121
+     * @return a new Zipper
122
+     */
123
+    Zipper insertAfter(Zipper toTheRight) {
124
+        this.right = toTheRight;
125
+        return this.right;
126
+    }
127
+
128
+    /**
129
+     * Removes the focus node and all subtrees
130
+     * Focus moves to the next node if possible otherwise to the prev node if possible, otherwise to the parent node
131
+     * @return a new zipper
132
+     */
133
+    Zipper delete() {
134
+        Zipper current = this;
135
+        if (current.up == null) {
136
+            current = null;
137
+        } else {
138
+            if (current.up.left == current) {
139
+                current = current.up;
140
+                current.left = null;
141
+                if (current.right != null) {
142
+                    current = current.right;
143
+                }
144
+            } else {
145
+                current = current.up;
146
+                current.right = null;
147
+                if (current.left != null) {
148
+                    current = current.left;
149
+                }
150
+            }
151
+        }
152
+        return current;
153
+    }
154
+
155
+}
156
+
157
+class BinaryTree {
158
+
159
+    private Zipper root;
160
+
161
+    BinaryTree(int value) {
162
+        root = new Zipper(value);
163
+        root.tree = this;
164
+    }
165
+
166
+    BinaryTree(Zipper root) {
167
+        this.root = root;
168
+        this.root.tree = this;
169
+    }
170
+
171
+    Zipper getRoot() {
172
+        return root;
173
+    }
174
+
175
+    void setRoot(Zipper zipper) {
176
+        root = zipper;
177
+        root.tree = this;
178
+    }
179
+
180
+    String printTree(Zipper zipper) {
181
+        String str = "";
182
+        if (zipper.left == null && zipper.right == null) {
183
+            str += "value: " + zipper.getValue() + ", left: " + null + ", right: " + null;
184
+        } else if (zipper.left == null) {
185
+            str += "value: " + zipper.getValue() + ", left: " + null + ", right: { " + printTree(zipper.right) + " }";
186
+        } else if (zipper.right == null) {
187
+            str += "value: " + zipper.getValue() + ", left: { " + printTree(zipper.left) + " }, right: " + null;
188
+        } else {
189
+            str += "value: " + zipper.getValue() +
190
+                    ", left: { " + printTree(zipper.left) + " }, right: { " + printTree(zipper.right) + " }";
191
+        }
192
+        return str;
193
+    }
194
+
195
+    String printTree() {
196
+        return printTree(root);
197
+    }
198
+
199
+}

+ 1
- 0
exercises/zipper/.meta/version 查看文件

1
+1.1.0

+ 44
- 0
exercises/zipper/README.md 查看文件

1
+# Zipper
2
+
3
+Creating a zipper for a binary tree.
4
+
5
+Zippers 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).
6
+
7
+Find out more about Zipper: https://en.wikipedia.org/wiki/Zipper_%28data_structure%29
8
+
9
+A zipper should be able to perform these operations:
10
+
11
+* `from_tree` (get a zipper out of a rose tree, the focus is on the root node)
12
+* `to_tree` (get the rose tree out of the zipper)
13
+* `value` (get the value of the focus node)
14
+* `prev` (move the focus to the previous child of the same parent, returns a new zipper)
15
+* `next` (move the focus to the next child of the same parent, returns a new zipper)
16
+* `up` (move the focus to the parent, returns a new zipper)
17
+* `left` (move the focus to the left child, returns a new zipper)
18
+* `right` (move the focus to the right child, returns a new zipper)
19
+* `set_value` (set the value of the focus node, returns a new zipper)
20
+* `set_left` (set the left child of the focus node, returns a new zipper)
21
+* `set_right` (set the right child of the focus node, returns a new zipper)
22
+* `insert_before` (insert a new subtree before the focus node, it becomes the prev of the focus node, returns a new zipper)
23
+* `insert_after` (insert a new subtree after the focus node, it becomes the next of the focus node, returns a new zipper)
24
+* `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)
25
+
26
+Together with zippers, implement binary tree objects that contains a zipper as the root.
27
+Trees are encoded as nested objects. Each node in the tree has three members: 'value', 'left', and 'right'. Each value is a number (for simplicity). Left and right are trees. An empty node is encoded as null.
28
+Implement `toString` method to print the tree as a single string.
29
+
30
+An example of a tree is as follow: tree: value: 1, left: { value: 2, left: null, right: { value: 3, left: null, right: null } }, right: { value: 4, left: null, right: null }
31
+
32
+# Running the tests
33
+
34
+You can run all the tests for an exercise by entering
35
+
36
+```sh
37
+$ gradle test
38
+```
39
+
40
+in your terminal.
41
+
42
+## Submitting Incomplete Solutions
43
+
44
+It's possible to submit an incomplete solution so you can see how others have completed the exercise.

+ 18
- 0
exercises/zipper/build.gradle 查看文件

1
+apply plugin: "java"
2
+apply plugin: "eclipse"
3
+apply plugin: "idea"
4
+
5
+repositories {
6
+  mavenCentral()
7
+}
8
+
9
+dependencies {
10
+  testCompile "junit:junit:4.12"
11
+}
12
+
13
+test {
14
+  testLogging {
15
+    exceptionFormat = 'full'
16
+    events = ["passed", "failed", "skipped"]
17
+  }
18
+}

+ 0
- 0
exercises/zipper/src/main/java/.keep 查看文件


+ 204
- 0
exercises/zipper/src/test/java/ZipperTest.java 查看文件

1
+import org.junit.Ignore;
2
+import org.junit.Test;
3
+import org.junit.Before;
4
+
5
+import static org.junit.Assert.assertEquals;
6
+
7
+public class ZipperTest {
8
+
9
+    Zipper zipper;
10
+    BinaryTree binaryTree;
11
+
12
+    @Before
13
+    public void setup() {
14
+        zipper = new Zipper(1);
15
+        binaryTree = new BinaryTree(zipper);
16
+
17
+        zipper.setLeft(new Zipper(2));
18
+        zipper.left.setRight(new Zipper(3));
19
+
20
+        zipper.setRight(new Zipper(4));
21
+    }
22
+
23
+    @Test
24
+    public void testToTree() {
25
+        assertEquals(binaryTree, zipper.toTree());
26
+    }
27
+
28
+    @Ignore("Remove to run test")
29
+    @Test
30
+    public void testLeftRightAndValue() {
31
+        zipper = binaryTree.getRoot();
32
+        assertEquals(3, zipper.left.right.getValue());
33
+    }
34
+
35
+    @Ignore("Remove to run test")
36
+    @Test
37
+    public void testDeadEnd() {
38
+        zipper = binaryTree.getRoot();
39
+        assertEquals(null, zipper.left.left);
40
+    }
41
+
42
+    @Ignore("Remove to run test")
43
+    @Test
44
+    public void testToTreeFromDeepFocus() {
45
+        zipper = binaryTree.getRoot();
46
+        assertEquals(binaryTree, zipper.left.right.toTree());
47
+    }
48
+
49
+    @Ignore("Remove to run test")
50
+    @Test
51
+    public void testTraversingUpFromTop() {
52
+        zipper = binaryTree.getRoot();
53
+        assertEquals(null, zipper.up);
54
+    }
55
+
56
+    @Ignore("Remove to run test")
57
+    @Test
58
+    public void testLeftRightAndUp() {
59
+        zipper = binaryTree.getRoot();
60
+        assertEquals(3, zipper.left.up.right.up.left.right.getValue());
61
+    }
62
+
63
+    @Ignore("Remove to run test")
64
+    @Test
65
+    public void testSetValue() {
66
+        zipper = binaryTree.getRoot();
67
+        zipper = zipper.left;
68
+        zipper.setValue(5);
69
+        String expected =
70
+                "value: 1, " +
71
+                "left: { " +
72
+                    "value: 5, " +
73
+                    "left: null, " +
74
+                    "right: { " +
75
+                        "value: 3, " +
76
+                        "left: null, " +
77
+                        "right: null } }, " +
78
+                "right: { " +
79
+                    "value: 4, " +
80
+                    "left: null, " +
81
+                    "right: null }";
82
+        assertEquals(expected, zipper.toTree().printTree());
83
+    }
84
+
85
+    @Ignore("Remove to run test")
86
+    @Test
87
+    public void testSetValueAfterTraversingUp() {
88
+        zipper = binaryTree.getRoot();
89
+        zipper = zipper.left.right.up;
90
+        zipper.setValue(5);
91
+        String expected =
92
+                "value: 1, " +
93
+                "left: { " +
94
+                    "value: 5, " +
95
+                    "left: null, " +
96
+                    "right: { " +
97
+                        "value: 3, " +
98
+                        "left: null, " +
99
+                        "right: null } }, " +
100
+                "right: { " +
101
+                    "value: 4, " +
102
+                    "left: null, " +
103
+                    "right: null }";
104
+        assertEquals(expected, zipper.toTree().printTree());
105
+    }
106
+
107
+    @Ignore("Remove to run test")
108
+    @Test
109
+    public void testSetLeftWithLeaf() {
110
+        zipper = binaryTree.getRoot();
111
+        zipper = zipper.left;
112
+        zipper.setLeft(new Zipper(5));
113
+        String expected =
114
+                "value: 1, " +
115
+                "left: { " +
116
+                    "value: 2, " +
117
+                    "left: { " +
118
+                        "value: 5, " +
119
+                        "left: null, " +
120
+                        "right: null }, " +
121
+                    "right: { " +
122
+                        "value: 3, " +
123
+                        "left: null, " +
124
+                        "right: null } }, " +
125
+                "right: { " +
126
+                    "value: 4, " +
127
+                    "left: null, " +
128
+                    "right: null }";
129
+        assertEquals(expected, zipper.toTree().printTree());
130
+    }
131
+
132
+    @Ignore("Remove to run test")
133
+    @Test
134
+    public void testSetRightWithNull() {
135
+        zipper = binaryTree.getRoot();
136
+        zipper = zipper.left;
137
+        zipper.setRight(null);
138
+        String expected =
139
+                "value: 1, " +
140
+                "left: { value: 2, left: null, right: null }, " +
141
+                "right: { value: 4, left: null, right: null }";
142
+        assertEquals(expected, zipper.toTree().printTree());
143
+    }
144
+
145
+    @Ignore("Remove to run test")
146
+    @Test
147
+    public void testSetRightWithSubtree() {
148
+        BinaryTree subtree = new BinaryTree(6);
149
+        subtree.getRoot().setLeft(new Zipper(7));
150
+        subtree.getRoot().setRight(new Zipper(8));
151
+        zipper = binaryTree.getRoot();
152
+        zipper.setRight(subtree.getRoot());
153
+        String expected =
154
+                "value: 1, " +
155
+                "left: { " +
156
+                    "value: 2, " +
157
+                    "left: null, " +
158
+                    "right: { " +
159
+                        "value: 3, " +
160
+                        "left: null, " +
161
+                        "right: null } }, " +
162
+                "right: { " +
163
+                    "value: 6, " +
164
+                    "left: { " +
165
+                        "value: 7, " +
166
+                        "left: null, " +
167
+                        "right: null }, " +
168
+                    "right: { " +
169
+                        "value: 8, " +
170
+                        "left: null, " +
171
+                        "right: null } }";
172
+        assertEquals(expected, zipper.toTree().printTree());
173
+    }
174
+
175
+    @Ignore("Remove to run test")
176
+    @Test
177
+    public void testSetValueOnDeepFocus() {
178
+        zipper = binaryTree.getRoot();
179
+        zipper = zipper.left.right;
180
+        zipper.setValue(5);
181
+        String expected =
182
+                "value: 1, " +
183
+                "left: { " +
184
+                    "value: 2, " +
185
+                    "left: null, " +
186
+                    "right: { " +
187
+                        "value: 5, " +
188
+                        "left: null, " +
189
+                        "right: null } }, " +
190
+                "right: { " +
191
+                    "value: 4, " +
192
+                    "left: null, " +
193
+                    "right: null }";
194
+        assertEquals(expected, zipper.toTree().printTree());
195
+    }
196
+
197
+    @Ignore("Remove to run test")
198
+    @Test
199
+    public void differentPathToSameZipper() {
200
+        Zipper zipper1 = binaryTree.getRoot().left.up.right;
201
+        Zipper zipper2 = binaryTree.getRoot().right;
202
+        assertEquals(zipper2, zipper1);
203
+    }
204
+}