Skip to content

Commit f99afa6

Browse files
ErikSchierboomrobkeim
authored andcommitted
Add zipper exercise (#179)
1 parent f8fb9cd commit f99afa6

4 files changed

Lines changed: 246 additions & 17 deletions

File tree

config.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,15 @@
814814
"Transforming"
815815
]
816816
},
817+
{
818+
"slug": "zipper",
819+
"difficulty": 10,
820+
"topics": [
821+
"Trees",
822+
"Recursion",
823+
"Searching"
824+
]
825+
},
817826
{
818827
"slug": "alphametics",
819828
"difficulty": 10,

exercises/pov/Example.cs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ public bool Equals(Graph<T> other) =>
1717
Value.Equals(other.Value) && Children.SequenceEqual(other.Children);
1818
}
1919

20-
public class Crumb<T>
20+
public class GraphCrumb<T>
2121
{
22-
public Crumb(T value, IEnumerable<Graph<T>> left, IEnumerable<Graph<T>> right)
22+
public GraphCrumb(T value, IEnumerable<Graph<T>> left, IEnumerable<Graph<T>> right)
2323
{
2424
Value = value;
2525
Left = left;
@@ -31,16 +31,16 @@ public Crumb(T value, IEnumerable<Graph<T>> left, IEnumerable<Graph<T>> right)
3131
public IEnumerable<Graph<T>> Right { get; }
3232
}
3333

34-
public class Zipper<T>
34+
public class GraphZipper<T>
3535
{
36-
public Zipper(Graph<T> focus, IEnumerable<Crumb<T>> crumbs)
36+
public GraphZipper(Graph<T> focus, IEnumerable<GraphCrumb<T>> crumbs)
3737
{
3838
Focus = focus;
3939
Crumbs = crumbs;
4040
}
4141

4242
public Graph<T> Focus { get; }
43-
public IEnumerable<Crumb<T>> Crumbs { get; }
43+
public IEnumerable<GraphCrumb<T>> Crumbs { get; }
4444
}
4545

4646
public static class Pov
@@ -54,49 +54,49 @@ public static Graph<T> FromPOV<T>(T value, Graph<T> graph) where T : IComparable
5454
public static IEnumerable<T> TracePathBetween<T>(T value1, T value2, Graph<T> graph) where T : IComparable
5555
=> ZipperToPath(FindNode(value2, GraphToZipper(FromPOV(value1, graph))));
5656

57-
private static Zipper<T> GraphToZipper<T>(Graph<T> graph)
57+
private static GraphZipper<T> GraphToZipper<T>(Graph<T> graph)
5858
{
5959
if (graph == null)
6060
return null;
6161

62-
return new Zipper<T>(graph, Enumerable.Empty<Crumb<T>>());
62+
return new GraphZipper<T>(graph, Enumerable.Empty<GraphCrumb<T>>());
6363
}
6464

65-
private static IEnumerable<T> ZipperToPath<T>(Zipper<T> zipper)
65+
private static IEnumerable<T> ZipperToPath<T>(GraphZipper<T> zipper)
6666
{
6767
if (zipper == null)
6868
return null;
6969

7070
return zipper.Crumbs.Select(c => c.Value).Reverse().Concat(new[] { zipper.Focus.Value });
7171
}
7272

73-
private static Zipper<T> GoDown<T>(Zipper<T> zipper)
73+
private static GraphZipper<T> GoDown<T>(GraphZipper<T> zipper)
7474
{
7575
if (zipper == null || !zipper.Focus.Children.Any())
7676
return null;
7777

7878
var focus = zipper.Focus;
7979
var children = focus.Children;
8080

81-
var newCrumb = new Crumb<T>(focus.Value, Enumerable.Empty<Graph<T>>(), children.Skip(1));
81+
var newCrumb = new GraphCrumb<T>(focus.Value, Enumerable.Empty<Graph<T>>(), children.Skip(1));
8282

83-
return new Zipper<T>(children.First(), new[] { newCrumb }.Concat(zipper.Crumbs));
83+
return new GraphZipper<T>(children.First(), new[] { newCrumb }.Concat(zipper.Crumbs));
8484
}
8585

86-
private static Zipper<T> GoRight<T>(Zipper<T> zipper)
86+
private static GraphZipper<T> GoRight<T>(GraphZipper<T> zipper)
8787
{
8888
if (zipper == null || !zipper.Crumbs.Any() || !zipper.Crumbs.First().Right.Any())
8989
return null;
9090

9191
var crumbs = zipper.Crumbs;
9292
var firstCrumb = crumbs.First();
9393

94-
var newCrumb = new Crumb<T>(firstCrumb.Value, firstCrumb.Left.Concat(new[] { zipper.Focus }), firstCrumb.Right.Skip(1));
94+
var newCrumb = new GraphCrumb<T>(firstCrumb.Value, firstCrumb.Left.Concat(new[] { zipper.Focus }), firstCrumb.Right.Skip(1));
9595

96-
return new Zipper<T>(firstCrumb.Right.First(), new[] { newCrumb }.Concat(crumbs.Skip(1)));
96+
return new GraphZipper<T>(firstCrumb.Right.First(), new[] { newCrumb }.Concat(crumbs.Skip(1)));
9797
}
9898

99-
private static Zipper<T> FindNode<T>(T value, Zipper<T> zipper)
99+
private static GraphZipper<T> FindNode<T>(T value, GraphZipper<T> zipper)
100100
where T : IComparable
101101
{
102102
if (zipper == null || zipper.Focus.Value.CompareTo(value) == 0)
@@ -105,7 +105,7 @@ private static Zipper<T> FindNode<T>(T value, Zipper<T> zipper)
105105
return FindNode(value, GoDown(zipper)) ?? FindNode(value, GoRight(zipper));
106106
}
107107

108-
private static Graph<T> ChangeParent<T>(Zipper<T> zipper)
108+
private static Graph<T> ChangeParent<T>(GraphZipper<T> zipper)
109109
{
110110
if (zipper == null)
111111
return null;
@@ -116,7 +116,7 @@ private static Graph<T> ChangeParent<T>(Zipper<T> zipper)
116116
var firstCrumb = zipper.Crumbs.First();
117117
var focus = zipper.Focus;
118118

119-
var newZipper = new Zipper<T>(CreateGraph(firstCrumb.Value, firstCrumb.Left.Concat(firstCrumb.Right)), zipper.Crumbs.Skip(1));
119+
var newZipper = new GraphZipper<T>(CreateGraph(firstCrumb.Value, firstCrumb.Left.Concat(firstCrumb.Right)), zipper.Crumbs.Skip(1));
120120
var parentGraph = ChangeParent(newZipper);
121121

122122
var ys = focus.Children.Concat(new[] { parentGraph });

exercises/zipper/Example.cs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
public class BinTree<T> : IEquatable<BinTree<T>>
6+
{
7+
public BinTree(T value, BinTree<T> left, BinTree<T> right)
8+
{
9+
Value = value;
10+
Left = left;
11+
Right = right;
12+
}
13+
14+
public BinTree(BinTree<T> tree) : this(tree.Value, tree.Left, tree.Right)
15+
{
16+
}
17+
18+
public T Value { get; }
19+
public BinTree<T> Left { get; }
20+
public BinTree<T> Right { get; }
21+
22+
public bool Equals(BinTree<T> other)
23+
{
24+
if (other == null || !Equals(Value, other.Value))
25+
return false;
26+
27+
if (!ReferenceEquals(Left, other.Left) && (!Left?.Equals(other.Left) ?? false))
28+
return false;
29+
30+
if (!ReferenceEquals(Right, other.Right) && (!Right?.Equals(other.Right) ?? false))
31+
return false;
32+
33+
return true;
34+
}
35+
}
36+
37+
public abstract class BinTreeCrumb<T>
38+
{
39+
public BinTreeCrumb(T value, BinTree<T> tree)
40+
{
41+
Value = value;
42+
Tree = tree;
43+
}
44+
45+
public T Value { get; }
46+
public BinTree<T> Tree { get; }
47+
}
48+
49+
public class BinTreeLeftCrumb<T> : BinTreeCrumb<T>
50+
{
51+
public BinTreeLeftCrumb(T value, BinTree<T> tree) : base(value, tree)
52+
{
53+
}
54+
}
55+
56+
public class BinTreeRightCrumb<T> : BinTreeCrumb<T>
57+
{
58+
public BinTreeRightCrumb(T value, BinTree<T> tree) : base(value, tree)
59+
{
60+
}
61+
}
62+
63+
public class Zipper<T>
64+
{
65+
private readonly T value;
66+
private readonly BinTree<T> left;
67+
private readonly BinTree<T> right;
68+
private readonly List<BinTreeCrumb<T>> crumbs;
69+
70+
public Zipper(T value, BinTree<T> left, BinTree<T> right, List<BinTreeCrumb<T>> crumbs)
71+
{
72+
this.value = value;
73+
this.left = left;
74+
this.right = right;
75+
this.crumbs = crumbs;
76+
}
77+
78+
public T Value => value;
79+
80+
public Zipper<T> SetValue(T newValue) => new Zipper<T>(newValue, left, right, crumbs);
81+
82+
public Zipper<T> SetLeft(BinTree<T> binTree) => new Zipper<T>(value, binTree, right, crumbs);
83+
84+
public Zipper<T> SetRight(BinTree<T> binTree) => new Zipper<T>(value, left, binTree, crumbs);
85+
86+
public Zipper<T> Left()
87+
{
88+
if (left == null)
89+
return null;
90+
91+
var newCrumbs = new[] { new BinTreeLeftCrumb<T>(value, right) }.Concat(crumbs).ToList();
92+
return new Zipper<T>(left.Value, left.Left, left.Right, newCrumbs);
93+
}
94+
95+
public Zipper<T> Right()
96+
{
97+
if (right == null)
98+
return null;
99+
100+
var newCrumbs = new[] { new BinTreeRightCrumb<T>(value, left) }.Concat(crumbs).ToList();
101+
return new Zipper<T>(right.Value, right.Left, right.Right, newCrumbs);
102+
}
103+
104+
public Zipper<T> Up()
105+
{
106+
if (crumbs.Count == 0)
107+
return null;
108+
109+
var firstCrumb = crumbs[0];
110+
var remainingCrumbs = crumbs.Skip(1).ToList();
111+
112+
if (firstCrumb is BinTreeLeftCrumb<T>)
113+
return new Zipper<T>(firstCrumb.Value, new BinTree<T>(value, left, right), firstCrumb.Tree, remainingCrumbs);
114+
115+
if (firstCrumb is BinTreeRightCrumb<T>)
116+
return new Zipper<T>(firstCrumb.Value, firstCrumb.Tree, new BinTree<T>(value, left, right), remainingCrumbs);
117+
118+
return null;
119+
}
120+
121+
public BinTree<T> ToTree()
122+
{
123+
var tree = new BinTree<T>(value, left, right);
124+
125+
foreach (var crumb in crumbs)
126+
{
127+
if (crumb is BinTreeLeftCrumb<T>)
128+
tree = new BinTree<T>(crumb.Value, new BinTree<T>(tree), crumb.Tree);
129+
if (crumb is BinTreeRightCrumb<T>)
130+
tree = new BinTree<T>(crumb.Value, crumb.Tree, new BinTree<T>(tree));
131+
}
132+
133+
return tree;
134+
}
135+
136+
public static Zipper<T> FromTree(BinTree<T> tree) => new Zipper<T>(tree.Value, tree.Left, tree.Right, new List<BinTreeCrumb<T>>());
137+
}

exercises/zipper/ZipperTest.cs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using NUnit.Framework;
2+
3+
public class ZipperTest
4+
{
5+
private static BinTree<int> bt(int v, BinTree<int> l, BinTree<int> r) => new BinTree<int>(v, l, r);
6+
private static BinTree<int> leaf(int v) => bt(v, null, null);
7+
8+
private static readonly BinTree<int> empty = null;
9+
private static readonly BinTree<int> t1 = new BinTree<int>(1, bt(2, empty, leaf(3)), leaf(4));
10+
private static readonly BinTree<int> t2 = new BinTree<int>(1, bt(5, empty, leaf(3)), leaf(4));
11+
private static readonly BinTree<int> t3 = new BinTree<int>(1, bt(2, leaf(5), leaf(3)), leaf(4));
12+
private static readonly BinTree<int> t4 = new BinTree<int>(1, leaf(2), leaf(4));
13+
14+
[Test]
15+
public void Data_is_retained()
16+
{
17+
var zipper = Zipper<int>.FromTree(t1);
18+
var tree = zipper.ToTree();
19+
Assert.That(tree, Is.EqualTo(t1));
20+
}
21+
22+
[Ignore("Remove to run test")]
23+
[Test]
24+
public void Left_right_and_value()
25+
{
26+
var zipper = Zipper<int>.FromTree(t1);
27+
Assert.That(zipper.Left().Right().Value, Is.EqualTo(3));
28+
}
29+
30+
[Ignore("Remove to run test")]
31+
[Test]
32+
public void Dead_end()
33+
{
34+
var zipper = Zipper<int>.FromTree(t1);
35+
Assert.That(zipper.Left().Left(), Is.Null);
36+
}
37+
38+
[Ignore("Remove to run test")]
39+
[Test]
40+
public void Tree_from_deep_focus()
41+
{
42+
var zipper = Zipper<int>.FromTree(t1);
43+
Assert.That(zipper.Left().Right().ToTree(), Is.EqualTo(t1));
44+
}
45+
46+
[Ignore("Remove to run test")]
47+
[Test]
48+
public void Set_value()
49+
{
50+
var zipper = Zipper<int>.FromTree(t1);
51+
var updatedZipper = zipper.Left().SetValue(5);
52+
var tree = updatedZipper.ToTree();
53+
Assert.That(tree, Is.EqualTo(t2));
54+
}
55+
56+
[Ignore("Remove to run test")]
57+
[Test]
58+
public void Set_left_with_value()
59+
{
60+
var zipper = Zipper<int>.FromTree(t1);
61+
var updatedZipper = zipper.Left().SetLeft(new BinTree<int>(5, null, null));
62+
var tree = updatedZipper.ToTree();
63+
Assert.That(tree, Is.EqualTo(t3));
64+
}
65+
66+
[Ignore("Remove to run test")]
67+
[Test]
68+
public void Set_right_to_null()
69+
{
70+
var zipper = Zipper<int>.FromTree(t1);
71+
var updatedZipper = zipper.Left().SetRight(null);
72+
var tree = updatedZipper.ToTree();
73+
Assert.That(tree, Is.EqualTo(t4));
74+
}
75+
76+
[Ignore("Remove to run test")]
77+
[Test]
78+
public void Different_paths_to_same_zipper()
79+
{
80+
var zipper = Zipper<int>.FromTree(t1);
81+
Assert.That(zipper.Left().Up().Right().ToTree(), Is.EqualTo(zipper.Right().ToTree()));
82+
}
83+
}

0 commit comments

Comments
 (0)