2017-08-15

This commit is contained in:
2017-08-15 09:55:28 -05:00
parent 549ba5084e
commit 4a9b690bb2
28 changed files with 1153 additions and 18 deletions

59
go/pov/README.md Normal file
View File

@@ -0,0 +1,59 @@
# Pov
Reparent a graph on a selected node
# Tree Reparenting
This exercise is all about re-orientating a graph to see things from a different
point of view. For example family trees are usually presented from the
ancestor's perspective:
```
+------0------+
| | |
+-1-+ +-2-+ +-3-+
| | | | | |
4 5 6 7 8 9
```
But the same information can be presented from the perspective of any other node
in the graph, by pulling it up to the root and dragging its relationships along
with it. So the same graph from 6's perspective would look like:
```
6
|
+-----2-----+
| |
7 +-----0-----+
| |
+-1-+ +-3-+
| | | |
4 5 8 9
```
This lets us more simply describe the paths between two nodes. So for example
the path from 6-9 (which in the first graph goes up to the root and then down to
a different leaf node) can be seen to follow the path 6-2-0-3-9
This exercise involves taking an input graph and re-orientating it from the point
of view of one of the nodes.
To run the tests simply run the command `go test` in the exercise directory.
If the test suite contains benchmarks, you can run these with the `-bench`
flag:
go test -bench .
For more detailed info about the Go track see the [help
page](http://exercism.io/languages/go).
## Source
Adaptation of exercise from 4clojure [https://www.4clojure.com/](https://www.4clojure.com/)
## Submitting Incomplete Problems
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

122
go/pov/pov.go Normal file
View File

@@ -0,0 +1,122 @@
package pov
const testVersion = 2
type Graph struct {
leaves []Node
}
func New() *Graph {
g := new(Graph)
return g
}
func (g *Graph) GetNode(lbl string) *Node {
for i := range g.leaves {
if n := g.leaves[i].GetNode(lbl); n != nil {
return n
}
}
return nil
}
func (g *Graph) AddNode(lbl string) {
g.leaves = append(g.leaves, Node{label: lbl})
}
func (g *Graph) addRealNode(n *Node) {
g.leaves = append(g.leaves, n)
}
func (g *Graph) AddArc(from, to string) {
if n := g.GetNode(to); n != nil {
n.AddNode(from)
}
}
func (g *Graph) ArcList() []string {
var ret []string
for i := range g.leaves {
ret = append(ret, g.leaves[i].ArcList()...)
}
return ret
}
func (g *Graph) ChangeRoot(oldRoot, newRoot string) *Graph {
// First of all, find the newRoot node
ret := New()
// The new graph will start with newRoot and have newRoot's leaves
if rt := g.GetNode(newRoot); rt == nil {
return ret
}
// It'll have one more leaf, it's parent node
ret.AddNode(rt)
return g
}
func (g *Graph) getPath(from, to string) []string {
var ret []string
// Get the 'from' node
frNode := g.GetNode(from)
if frNode == nil {
// Couldn't find the starting node
return ret
}
// Just in case we got the same value for both
if from == to {
return []string{from}
}
// Found it
return frNode.getPath(to)
}
type Node struct {
label string
leaves []Node
}
func (n *Node) AddNode(lbl string) {
n.leaves = append(n.leaves, Node{label: lbl})
}
func (n *Node) GetNode(lbl string) *Node {
if n.label == lbl {
return n
}
for i := range n.leaves {
if r := n.leaves[i].GetNode(lbl); r != nil {
return r
}
}
return nil
}
func (n *Node) ArcList() []string {
var ret []string
for i := range n.leaves {
ret = append(ret, n.leaves[i].label+" -> "+n.label)
ret = append(ret, n.leaves[i].ArcList()...)
}
return ret
}
func (n *Node) getPath(to string) []string {
ret := []string{n.label}
if n.label == to {
return ret
}
var i int
var found bool
for i = range n.leaves {
if n.leaves[i].GetNode(to) != nil {
found = true
break
}
}
if !found {
// We didn't find a path... :(
return ret
}
// n.leaves[i] should be the right leaf now
return append(ret, n.leaves[i].getPath(to)...)
}

295
go/pov/pov_test.go Normal file
View File

@@ -0,0 +1,295 @@
package pov
import (
"reflect"
"sort"
"testing"
)
const targetTestVersion = 2
// POV / reparent / change root of a tree
//
// API:
// type Graph
// func New() *Graph
// func (*Graph) AddNode(nodeLabel string)
// func (*Graph) AddArc(from, to string)
// func (*Graph) ArcList() []string
// func (*Graph) ChangeRoot(oldRoot, newRoot string) *Graph
//
// The type name is Graph because you'll probably be implementing a general
// directed graph representation, although the test program will only use
// it to create a tree. The term "arc" is used here to mean a directed edge.
//
// The test program will create a graph with New, then use AddNode to add
// leaf nodes. After that it will use AddArc to construct the rest of the tree
// from the bottom up. That is, the `to` argument will aways specify a node
// that has already been added.
//
// ArcList is a dump method to let the test program see your graph. It must
// return a list of all arcs in the graph. Format each arc as a single string
// like "from -> to". The test program can then easily sort the list and
// compare it to an expected result. You do not need to bother with sorting
// the list yourself.
//
// All this graph construction and dumping must be working before you start
// on the interesting part of the exercise, so it is tested separately as
// a first test.
//
// API function ChangeRoot does the interesting part of the exercise.
// OldRoot is passed (as a convenience) and you must return a graph with
// newRoot as the root. You can modify the original graph in place and
// return it or create a new graph and return that. If you return a new
// graph you are free to consume or destroy the original graph. Of course
// it's nice to leave it unmodified.
type arc struct{ fr, to string }
type testCase struct {
description string
leaves []string
arcPairs []arc
root string
arcStrings []string
reRooted []string
}
var testCases = []testCase{
{
description: "singleton",
leaves: []string{"x"},
arcPairs: nil,
root: "x",
arcStrings: nil,
reRooted: nil,
},
{
description: "simple tree",
leaves: []string{"sibling", "x"},
arcPairs: []arc{
{"parent", "sibling"},
{"parent", "x"},
},
root: "parent",
arcStrings: []string{
"parent -> sibling",
"parent -> x",
},
reRooted: []string{
"parent -> sibling",
"x -> parent",
},
},
{
description: "large flat",
leaves: []string{"sib-a", "sib-b", "x", "sib-c", "sib-d"},
arcPairs: []arc{
{"parent", "sib-a"},
{"parent", "sib-b"},
{"parent", "x"},
{"parent", "sib-c"},
{"parent", "sib-d"},
},
root: "parent",
arcStrings: []string{
"parent -> sib-a",
"parent -> sib-b",
"parent -> sib-c",
"parent -> sib-d",
"parent -> x",
},
reRooted: []string{
"parent -> sib-a",
"parent -> sib-b",
"parent -> sib-c",
"parent -> sib-d",
"x -> parent",
},
},
{
description: "deeply nested",
leaves: []string{"x"},
arcPairs: []arc{
{"level-4", "x"},
{"level-3", "level-4"},
{"level-2", "level-3"},
{"level-1", "level-2"},
{"level-0", "level-1"},
},
root: "level-0",
arcStrings: []string{
"level-0 -> level-1",
"level-1 -> level-2",
"level-2 -> level-3",
"level-3 -> level-4",
"level-4 -> x",
},
reRooted: []string{
"level-1 -> level-0",
"level-2 -> level-1",
"level-3 -> level-2",
"level-4 -> level-3",
"x -> level-4",
},
},
{
description: "cousins",
leaves: []string{"sib-1", "x", "sib-2", "cousin-1", "cousin-2"},
arcPairs: []arc{
{"parent", "sib-1"},
{"parent", "x"},
{"parent", "sib-2"},
{"aunt", "cousin-1"},
{"aunt", "cousin-2"},
{"grand-parent", "parent"},
{"grand-parent", "aunt"},
},
root: "grand-parent",
arcStrings: []string{
"aunt -> cousin-1",
"aunt -> cousin-2",
"grand-parent -> aunt",
"grand-parent -> parent",
"parent -> sib-1",
"parent -> sib-2",
"parent -> x",
},
reRooted: []string{
"aunt -> cousin-1",
"aunt -> cousin-2",
"grand-parent -> aunt",
"parent -> grand-parent",
"parent -> sib-1",
"parent -> sib-2",
"x -> parent",
},
},
{
description: "target with children",
leaves: []string{"child-1", "child-2", "nephew", "niece",
"2nd-cousin-1", "2nd-cousin-2", "2nd-cousin-3", "2nd-cousin-4"},
arcPairs: []arc{
{"x", "child-1"},
{"x", "child-2"},
{"sibling", "nephew"},
{"sibling", "niece"},
{"cousin-1", "2nd-cousin-1"},
{"cousin-1", "2nd-cousin-2"},
{"cousin-2", "2nd-cousin-3"},
{"cousin-2", "2nd-cousin-4"},
{"parent", "x"},
{"parent", "sibling"},
{"aunt", "cousin-1"},
{"aunt", "cousin-2"},
{"grand-parent", "parent"},
{"grand-parent", "aunt"},
},
root: "grand-parent",
arcStrings: []string{
"aunt -> cousin-1",
"aunt -> cousin-2",
"cousin-1 -> 2nd-cousin-1",
"cousin-1 -> 2nd-cousin-2",
"cousin-2 -> 2nd-cousin-3",
"cousin-2 -> 2nd-cousin-4",
"grand-parent -> aunt",
"grand-parent -> parent",
"parent -> sibling",
"parent -> x",
"sibling -> nephew",
"sibling -> niece",
"x -> child-1",
"x -> child-2",
},
reRooted: []string{
"aunt -> cousin-1",
"aunt -> cousin-2",
"cousin-1 -> 2nd-cousin-1",
"cousin-1 -> 2nd-cousin-2",
"cousin-2 -> 2nd-cousin-3",
"cousin-2 -> 2nd-cousin-4",
"grand-parent -> aunt",
"parent -> grand-parent",
"parent -> sibling",
"sibling -> nephew",
"sibling -> niece",
"x -> child-1",
"x -> child-2",
"x -> parent",
},
},
}
func (tc testCase) graph() *Graph {
g := New()
for _, l := range tc.leaves {
g.AddNode(l)
}
for _, a := range tc.arcPairs {
g.AddArc(a.fr, a.to)
}
return g
}
func (tc testCase) testResult(got, want []string, msg string, t *testing.T) {
if len(got)+len(want) == 0 {
return
}
gs := append([]string{}, got...)
sort.Strings(gs)
if reflect.DeepEqual(gs, want) {
return
}
// test has failed
t.Log(tc.description, "test case")
t.Log(msg)
t.Logf("got %d arcs:", len(got))
for _, s := range got {
t.Log(" ", s)
}
t.Logf("that result sorted:")
for _, s := range gs {
t.Log(" ", s)
}
t.Logf("want %d arcs:", len(want))
for _, s := range want {
t.Log(" ", s)
}
t.FailNow()
}
func TestConstruction(t *testing.T) {
if testVersion != targetTestVersion {
t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
}
for _, tc := range testCases {
got := tc.graph().ArcList()
want := tc.arcStrings
tc.testResult(got, want, "incorrect graph construction", t)
}
}
func TestChangeRoot(t *testing.T) {
for _, tc := range testCases {
got := tc.graph().ChangeRoot(tc.root, "x").ArcList()
want := tc.reRooted
tc.testResult(got, want, "incorrect root change", t)
}
}
func BenchmarkConstructOnlyNoChange(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, tc := range testCases {
tc.graph()
}
}
}
func BenchmarkConstructAndChangeRoot(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, tc := range testCases {
tc.graph().ChangeRoot(tc.root, "x")
}
}
}