From 4a9b690bb26820f92e53fc313adbf9714c6744e0 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Tue, 15 Aug 2017 09:55:28 -0500 Subject: [PATCH] 2017-08-15 --- go/accumulate/README.md | 53 ++++ go/accumulate/accumulate.go | 11 + go/accumulate/accumulate_test.go | 57 ++++ go/error-handling/README.md | 31 ++ go/error-handling/common.go | 57 ++++ go/error-handling/error_handling.go | 35 +++ go/error-handling/error_handling_test.go | 191 ++++++++++++ go/paasio/paasio.go | 113 ++++++- go/pov/README.md | 59 ++++ go/pov/pov.go | 122 ++++++++ go/pov/pov_test.go | 295 ++++++++++++++++++ python/current | 1 - .../__pycache__/example.cpython-36.pyc | Bin 0 -> 240 bytes .../example_test.cpython-36-PYTEST.pyc | Bin 0 -> 1322 bytes python/hello-world/.cache/v/cache/lastfailed | 1 + .../hello_world.cpython-36-PYTEST.pyc | Bin 0 -> 369 bytes .../__pycache__/hello_world.cpython-36.pyc | Bin 0 -> 282 bytes .../hello_world_test.cpython-36-PYTEST.pyc | Bin 0 -> 1177 bytes python/hello-world/hello_world.py | 4 +- python/isogram/README.md | 30 ++ python/isogram/isogram.py | 2 + python/isogram/isogram_test.py | 39 +++ python/leap/.cache/v/cache/lastfailed | 1 + python/leap/README.md | 44 +++ python/leap/__pycache__/leap.cpython-36.pyc | Bin 0 -> 289 bytes .../leap_test.cpython-36-PYTEST.pyc | Bin 0 -> 1323 bytes python/leap/leap.py | 2 + python/leap/leap_test.py | 23 ++ 28 files changed, 1153 insertions(+), 18 deletions(-) create mode 100644 go/accumulate/README.md create mode 100644 go/accumulate/accumulate.go create mode 100644 go/accumulate/accumulate_test.go create mode 100644 go/error-handling/README.md create mode 100644 go/error-handling/common.go create mode 100644 go/error-handling/error_handling.go create mode 100644 go/error-handling/error_handling_test.go create mode 100644 go/pov/README.md create mode 100644 go/pov/pov.go create mode 100644 go/pov/pov_test.go delete mode 120000 python/current create mode 100644 python/example/__pycache__/example.cpython-36.pyc create mode 100644 python/example/__pycache__/example_test.cpython-36-PYTEST.pyc create mode 100644 python/hello-world/.cache/v/cache/lastfailed create mode 100644 python/hello-world/__pycache__/hello_world.cpython-36-PYTEST.pyc create mode 100644 python/hello-world/__pycache__/hello_world.cpython-36.pyc create mode 100644 python/hello-world/__pycache__/hello_world_test.cpython-36-PYTEST.pyc create mode 100644 python/isogram/README.md create mode 100644 python/isogram/isogram.py create mode 100644 python/isogram/isogram_test.py create mode 100644 python/leap/.cache/v/cache/lastfailed create mode 100644 python/leap/README.md create mode 100644 python/leap/__pycache__/leap.cpython-36.pyc create mode 100644 python/leap/__pycache__/leap_test.cpython-36-PYTEST.pyc create mode 100644 python/leap/leap.py create mode 100644 python/leap/leap_test.py diff --git a/go/accumulate/README.md b/go/accumulate/README.md new file mode 100644 index 0000000..ccde5e6 --- /dev/null +++ b/go/accumulate/README.md @@ -0,0 +1,53 @@ +# Accumulate + +Implement the `accumulate` operation, which, given a collection and an +operation to perform on each element of the collection, returns a new +collection containing the result of applying that operation to each element of +the input collection. + +Given the collection of numbers: + +- 1, 2, 3, 4, 5 + +And the operation: + +- square a number (`x => x * x`) + +Your code should be able to produce the collection of squares: + +- 1, 4, 9, 16, 25 + +Check out the test suite to see the expected function signature. + +## Restrictions + +Keep your hands off that collect/map/fmap/whatchamacallit functionality +provided by your standard library! +Solve this one yourself using other basic tools instead. + +Lisp specific: it's perfectly fine to use `MAPCAR` or the equivalent, +as this is idiomatic Lisp, not a library function. + +## Running the tests + +To run the tests run the command `go test` from within the exercise directory. + +If the test suite contains benchmarks, you can run these with the `-bench` +flag: + + go test -bench . + +Keep in mind that each reviewer will run benchmarks on a different machine, with +different specs, so the results from these benchmark tests may vary. + +## Further information + +For more detailed information about the Go track, including how to get help if +you're having trouble, please visit the exercism.io [Go language page](http://exercism.io/languages/go/about). + +## Source + +Conversation with James Edward Gray II [https://twitter.com/jeg2](https://twitter.com/jeg2) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/go/accumulate/accumulate.go b/go/accumulate/accumulate.go new file mode 100644 index 0000000..f95a53f --- /dev/null +++ b/go/accumulate/accumulate.go @@ -0,0 +1,11 @@ +package accumulate + +const testVersion = 1 + +func Accumulate(vals []string, f func(string) string) []string { + ret := make([]string, len(vals)) + for i := range vals { + ret[i] = f(vals[i]) + } + return ret +} diff --git a/go/accumulate/accumulate_test.go b/go/accumulate/accumulate_test.go new file mode 100644 index 0000000..f9854e4 --- /dev/null +++ b/go/accumulate/accumulate_test.go @@ -0,0 +1,57 @@ +package accumulate + +import ( + "fmt" + "strings" + "testing" +) + +const targetTestVersion = 1 + +func echo(c string) string { + return c +} + +func capitalize(word string) string { + return strings.Title(word) +} + +var tests = []struct { + expected []string + given []string + converter func(string) string + description string +}{ + {[]string{}, []string{}, echo, "echo"}, + {[]string{"echo", "echo", "echo", "echo"}, []string{"echo", "echo", "echo", "echo"}, echo, "echo"}, + {[]string{"First", "Letter", "Only"}, []string{"first", "letter", "only"}, capitalize, "capitalize"}, + {[]string{"HELLO", "WORLD"}, []string{"hello", "world"}, strings.ToUpper, "strings.ToUpper"}, +} + +func TestTestVersion(t *testing.T) { + if testVersion != targetTestVersion { + t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion) + } +} + +func TestAccumulate(t *testing.T) { + for _, test := range tests { + actual := Accumulate(test.given, test.converter) + if fmt.Sprintf("%q", actual) != fmt.Sprintf("%q", test.expected) { + t.Fatalf("Accumulate(%q, %q): expected %q, actual %q", test.given, test.description, test.expected, actual) + } + } +} + +func BenchmarkAccumulate(b *testing.B) { + b.StopTimer() + for _, test := range tests { + b.StartTimer() + + for i := 0; i < b.N; i++ { + Accumulate(test.given, test.converter) + } + + b.StopTimer() + } +} diff --git a/go/error-handling/README.md b/go/error-handling/README.md new file mode 100644 index 0000000..c8a897f --- /dev/null +++ b/go/error-handling/README.md @@ -0,0 +1,31 @@ +# Error Handling + +Implement various kinds of error handling and resource management. + +An important point of programming is how to handle errors and close +resources even if errors occur. + +This exercise requires you to handle various errors. Because error handling +is rather programming language specific you'll have to refer to the tests +for your track to see what's exactly required. + +## Running the tests + +To run the tests run the command `go test` from within the exercise directory. + +If the test suite contains benchmarks, you can run these with the `-bench` +flag: + + go test -bench . + +Keep in mind that each reviewer will run benchmarks on a different machine, with +different specs, so the results from these benchmark tests may vary. + +## Further information + +For more detailed information about the Go track, including how to get help if +you're having trouble, please visit the exercism.io [Go language page](http://exercism.io/languages/go/about). + + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/go/error-handling/common.go b/go/error-handling/common.go new file mode 100644 index 0000000..9b3c8bb --- /dev/null +++ b/go/error-handling/common.go @@ -0,0 +1,57 @@ +package erratum + +import "io" + +// These are the support types and interface definitions used in the +// implementation if your Use function. See the test suite file at +// for information on the expected implementation. +// +// Because this is part of the package "erratum", if your solution file +// is also declared in the package you will automatically have access to +// these definitions (you do not have to re-declare them). + +// TransientError is an error that may occur while opening a resource via +// ResourceOpener. +type TransientError struct { + err error +} + +func (e TransientError) Error() string { + return e.err.Error() +} + +// FrobError is a possible error from doing some frobbing, your implementation +// will require calling your Resource's Defrob(string) method. +// When this error occurs, the FrobError's defrobTag string will contain the +// string you must pass into Defrob. +type FrobError struct { + defrobTag string + inner error +} + +func (e FrobError) Error() string { + return e.inner.Error() +} + +type Resource interface { + + // Resource is using composition to inherit the requirements of the io.Closer + // interface. What this means is that a Resource will have a .Close() method. + io.Closer + + // Frob does something with the input string. + // Because this is an incredibly badly designed system if there is an error + // it panics. + // + // The paniced error may be a FrobError in which case Defrob should be called + // with the defrobTag string. + Frob(string) + + Defrob(string) +} + +// ResourceOpener is a function that creates a resource. +// +// It may return a wrapped error of type TransientError. In this case the resource +// is temporarily unavailable and the caller should retry soon. +type ResourceOpener func() (Resource, error) diff --git a/go/error-handling/error_handling.go b/go/error-handling/error_handling.go new file mode 100644 index 0000000..176c1c0 --- /dev/null +++ b/go/error-handling/error_handling.go @@ -0,0 +1,35 @@ +package erratum + +const testVersion = 2 + +func Use(o ResourceOpener, inp string) (err error) { + var r Resource + moveOn := false + for !moveOn { + moveOn = true + if r, err = o(); err != nil { + switch err.(type) { + case TransientError: + moveOn = false + default: + return err + } + } + } + defer r.Close() + defer func() { + if rec := recover(); rec != nil { + switch v := rec.(type) { + case FrobError: + r.Defrob(v.defrobTag) + err = v + case error: + err = v + } + } + }() + + r.Frob(inp) + + return err +} diff --git a/go/error-handling/error_handling_test.go b/go/error-handling/error_handling_test.go new file mode 100644 index 0000000..122a960 --- /dev/null +++ b/go/error-handling/error_handling_test.go @@ -0,0 +1,191 @@ +package erratum + +import ( + "errors" + "testing" +) + +// Because this exercise is generally unique to each language and how it +// handles errors, most of the definition of your expected solution is provided +// here instead of the README. +// You should read this carefully (more than once) before implementation. + +// Define a function `Use(o ResourceOpener, input string) error` that opens a +// resource, calls Frob(input) and closes the resource (in all cases). Your +// function should properly handle errors, as defined by the expectations of +// this test suite. ResourceOpener will be a function you may invoke directly +// `o()` in an attempt to "open" the resource. It returns a Resource and error +// value in the idiomatic Go fashion: +// https://blog.golang.org/error-handling-and-go +// +// See the ./common.go file for the definitions of Resource, ResourceOpener, +// FrobError and TransientError. +// +// There will be a few places in your Use function where errors may occur: +// +// - Invoking the ResourceOpener function passed into Use as the first +// parameter, it may fail with a TransientError, if so keep trying to open it. +// If it is some other sort of error, return it. +// +// - Calling the Frob function on the Resource returned from the ResourceOpener +// function, it may panic with a FrobError (or another type of error). If +// it is indeed a FrobError you will have to call the Resource's Defrob +// function using the FrobError's defrobTag variable as input. Either way +// return the error. +// +// Also note: if the Resource was opened successfully make sure to call its +// Close function no matter what (even if errors occur). +// +// If you are new to Go errors or panics here is a good place to start: +// https://blog.golang.org/defer-panic-and-recover +// +// You may also need to look at named return values as a helpful way to +// return error information from panic recovery: +// https://tour.golang.org/basics/7 + +const targetTestVersion = 2 + +// Little helper to let us customize behaviour of the resource on a per-test +// basis. +type mockResource struct { + close func() error + frob func(string) + defrob func(string) +} + +func (mr mockResource) Close() error { return mr.close() } +func (mr mockResource) Frob(input string) { mr.frob(input) } +func (mr mockResource) Defrob(tag string) { mr.defrob(tag) } + +func TestTestVersion(t *testing.T) { + if testVersion != targetTestVersion { + t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion) + } +} + +// Use should not return an error on the "happy" path. +func TestNoErrors(t *testing.T) { + var frobInput string + var closeCalled bool + mr := mockResource{ + close: func() error { closeCalled = true; return nil }, + frob: func(input string) { frobInput = input }, + } + opener := func() (Resource, error) { return mr, nil } + inp := "hello" + err := Use(opener, inp) + if err != nil { + t.Fatalf("Unexpected error from Use: %v", err) + } + if frobInput != inp { + t.Fatalf("Wrong string passed to Frob: got %v, expected %v", frobInput, inp) + } + if !closeCalled { + t.Fatalf("Close was not called") + } +} + +// Use should keep trying if a transient error is returned on open. +func TestKeepTryOpenOnTransient(t *testing.T) { + var frobInput string + mr := mockResource{ + close: func() error { return nil }, + frob: func(input string) { frobInput = input }, + } + nthCall := 0 + opener := func() (Resource, error) { + if nthCall < 3 { + nthCall++ + return mockResource{}, TransientError{errors.New("some error")} + } + return mr, nil + } + inp := "hello" + err := Use(opener, inp) + if err != nil { + t.Fatalf("Unexpected error from Use: %v", err) + } + if frobInput != inp { + t.Fatalf("Wrong string passed to Frob: got %v, expected %v", frobInput, inp) + } +} + +// Use should fail if a non-transient error is returned on open. +func TestFailOpenOnNonTransient(t *testing.T) { + nthCall := 0 + opener := func() (Resource, error) { + if nthCall < 3 { + nthCall++ + return mockResource{}, TransientError{errors.New("some error")} + } + return nil, errors.New("too awesome") + } + inp := "hello" + err := Use(opener, inp) + if err == nil { + t.Fatalf("Unexpected lack of error from Use") + } + if err.Error() != "too awesome" { + t.Fatalf("Invalid error returned from Use") + } +} + +// Use should call Defrob and Close on FrobError panic from Frob +// and return the error. +func TestCallDefrobAndCloseOnFrobError(t *testing.T) { + tag := "moo" + var closeCalled bool + var defrobTag string + mr := mockResource{ + close: func() error { closeCalled = true; return nil }, + frob: func(input string) { panic(FrobError{tag, errors.New("meh")}) }, + defrob: func(tag string) { + if closeCalled { + t.Fatalf("Close was called before Defrob") + } + defrobTag = tag + }, + } + opener := func() (Resource, error) { return mr, nil } + inp := "hello" + err := Use(opener, inp) + if err == nil { + t.Fatalf("Unexpected lack of error from Use") + } + if err.Error() != "meh" { + t.Fatalf("Invalid error returned from Use") + } + if defrobTag != tag { + t.Fatalf("Wrong string passed to Defrob: got %v, expected %v", defrobTag, tag) + } + if !closeCalled { + t.Fatalf("Close was not called") + } +} + +// Use should call Close but not Defrob on non-FrobError panic from Frob +// and return the error. +func TestCallCloseNonOnFrobError(t *testing.T) { + var closeCalled bool + var defrobCalled bool + mr := mockResource{ + close: func() error { closeCalled = true; return nil }, + frob: func(input string) { panic(errors.New("meh")) }, + defrob: func(tag string) { defrobCalled = true }, + } + opener := func() (Resource, error) { return mr, nil } + inp := "hello" + err := Use(opener, inp) + if err == nil { + t.Fatalf("Unexpected lack of error from Use") + } + if err.Error() != "meh" { + t.Fatalf("Invalid error returned from Use") + } + if defrobCalled { + t.Fatalf("Defrob was called") + } + if !closeCalled { + t.Fatalf("Close was not called") + } +} diff --git a/go/paasio/paasio.go b/go/paasio/paasio.go index 7c0dadb..762f995 100644 --- a/go/paasio/paasio.go +++ b/go/paasio/paasio.go @@ -1,26 +1,109 @@ package paasio +import ( + "io" + "sync" +) + const testVersion = 3 -func NewWriteCounter() WriteCounter { - w := new(WriteCounter) - return w +func NewReadCounter(r io.Reader) ReadCounter { + return &readCounter{ + r: r, + lock: new(sync.Mutex), + } } -func (w *WriteCounter) WriteCount() (int64, int) { +type readCounter struct { + r io.Reader + bytesRead int64 + ops int + lock *sync.Mutex +} + +func (rc *readCounter) Read(p []byte) (int, error) { + m, err := rc.r.Read(p) + rc.lock.Lock() + rc.bytesRead += int64(m) + rc.ops++ + rc.lock.Unlock() + return m, err +} + +func (rc *readCounter) ReadCount() (n int64, ops int) { + rc.lock.Lock() + n, ops = rc.bytesRead, rc.ops + rc.lock.Unlock() + return n, ops +} + +func NewWriteCounter(w io.Writer) WriteCounter { + return &writeCounter{ + w: w, + lock: new(sync.Mutex), + } +} + +type writeCounter struct { + w io.Writer + bytesWrote int64 + ops int + lock *sync.Mutex +} + +func (wc *writeCounter) Write(p []byte) (int, error) { + m, err := wc.w.Write(p) + wc.lock.Lock() + wc.bytesWrote += int64(m) + wc.ops++ + wc.lock.Unlock() + return m, err +} + +func (wc *writeCounter) WriteCount() (n int64, ops int) { + wc.lock.Lock() + n, ops = wc.bytesWrote, wc.ops + wc.lock.Unlock() + return n, ops +} + +type ReadWriter interface { + io.Reader + io.Writer +} + +func NewReadWriteCounter(rw ReadWriter) ReadWriteCounter { + return &readWriteCounter{ + r: NewReadCounter(rw), + w: NewWriteCounter(rw), + } +} + +type readWriteCounter struct { + r ReadCounter + w WriteCounter +} + +func (rw *readWriteCounter) Read(p []byte) (int, error) { + return rw.r.Read(p) +} + +func (rw *readWriteCounter) Write(p []byte) (int, error) { + return rw.w.Write(p) +} + +func (rw *readWriteCounter) ReadCount() (n int64, ops int) { + return rw.r.ReadCount() +} + +func (rw *readWriteCounter) WriteCount() (n int64, ops int) { + return rw.w.WriteCount() +} + +func (nr *nopReader) ReadCount() (n int64, ops int) { return 0, 0 } -func NewReadCounter() ReadCounter { - r := new(ReadCounter) - return r -} - -func (r *ReadCounter) ReadCount() (int64, int) { +func (nw *nopWriter) WriteCount() (n int64, ops int) { return 0, 0 } - -func NewReadWriteCounter() *ReadWriteCounter { - r := new(ReadWriteCounter) - return r -} diff --git a/go/pov/README.md b/go/pov/README.md new file mode 100644 index 0000000..0e89617 --- /dev/null +++ b/go/pov/README.md @@ -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. + diff --git a/go/pov/pov.go b/go/pov/pov.go new file mode 100644 index 0000000..38be5a5 --- /dev/null +++ b/go/pov/pov.go @@ -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)...) +} diff --git a/go/pov/pov_test.go b/go/pov/pov_test.go new file mode 100644 index 0000000..b050c37 --- /dev/null +++ b/go/pov/pov_test.go @@ -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") + } + } +} diff --git a/python/current b/python/current deleted file mode 120000 index 96236f8..0000000 --- a/python/current +++ /dev/null @@ -1 +0,0 @@ -example \ No newline at end of file diff --git a/python/example/__pycache__/example.cpython-36.pyc b/python/example/__pycache__/example.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dc1b5a802741818f94fb45a818bfc5ede58539fb GIT binary patch literal 240 zcmXr!<>m6*wkKSLfq~&M5W@izkmUfx#XLYFg&~Epg&~R|g(;XplQ|hA2Ld1(gq?x7 zm;*@EFw`)LGXT{v`mJQpWV*#zaf`9Ch!H3QCVtuKXXNLm>L(Q?mFDE67U{dBmZj$8 z7v!eqmFTBdq!uM-7U$|0RF-7q=K=YNxdl0?Fj}vm@)mPqN(#uZVz2`ki&%kdKTQaY aum!?m0m|Ltu*uC&Da}c>1M32cFaQ8SeK92f literal 0 HcmV?d00001 diff --git a/python/example/__pycache__/example_test.cpython-36-PYTEST.pyc b/python/example/__pycache__/example_test.cpython-36-PYTEST.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aa5957735e4cc6487f3fb3c09f8aab8ddfbaa245 GIT binary patch literal 1322 zcmbVM&2G~`5Z<+&c#}9)s8IQlDh_b5gp#&!M5v-v+;V^lBwtoecB?wrPP%JDgSe%S zz)SD~oOqJGa`G#1Ak5fJ2vrVIyxMPO*6W#XW@r7Z-EQ`O{QU6EBIFlYc^uG>Ve0n) zf(ROuga(wNZpBt$m(m&4M)klgbr!HvHiE_*B5dJY6X8fUYR>GSB|TC5XsNegw`T;r zSnr5mFTgkPsp;ftG#m@b<0z9VjCFsAZx#=5tGGM`MUsFD5?F!?>zV+MGE(4(ny7!JY>n#Mc53X@4J&!o=uklwgNS%Y~3Q{M;pN=A@lMn{%_F@u4bf(N@_U?~dZ z;M=AZYAscEdYOl@ab@M(*r5=RhNESC{+*~M?EU?!cQH<6Z=eQw981-EEkDV4JV|7l z_2iXQ!$>E+$uzqdr(l;H7m{Z{wmX@c4k{d+^CKRnfGzN>>i~p0)S_SZSF`Kh-o0VE z_DZ@=zjy-P3l9Mc~jBhbR|S7 z1OguF7IY5|%Gw^@+mP;J|5kJrl2q7w#h>|nzA4?kWp%6KCQ+Jeezbp!^5gXt{_mT< z?;6H=8YYr+)8srEi#$f%;~d+%dZVxdR0rWM!XCoEGcPqfcx3}%LuhOcX0bYsauvfQ zVU%+2?_i+wJj)dXQh1jP8{|=(MX5I4@np)E)&mZg0{g$ZY0)_CD*0K#kHIvSOU7NS zHR0o`ILa{ijbvz)-D=nws3#%ZSLCShe2s&I*5zT?i>eSWakJh5@F*i5reyCvWb`k> Cp){ZX literal 0 HcmV?d00001 diff --git a/python/hello-world/.cache/v/cache/lastfailed b/python/hello-world/.cache/v/cache/lastfailed new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/python/hello-world/.cache/v/cache/lastfailed @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/python/hello-world/__pycache__/hello_world.cpython-36-PYTEST.pyc b/python/hello-world/__pycache__/hello_world.cpython-36-PYTEST.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f7ca8e2dc0447a420d4bb1f7025241c255ec4946 GIT binary patch literal 369 zcmYLFy-ve05I#3Q4ORO9tZW&G2UdhC0Ru}HB!&nnO6rr6%84U8p=9S}cn97C$tx4D zz{J@V;iT`o@6Km`wy!pu)x+n<(=z~k5#|U;Z3*UrNPu7xa^~~m5FElh5|Ic*_{Q>B zut!MaZ<>Rhg*|hE!exhBf(eNP&>Y*n6WOpUThz|P)6YGxRg1jR)hH!ueupn8Ro`Ma zaC}9*Z%oVkX{c4lYm`!59+j5jqzkXJezK8Yi=E?w2$*&{W!hEg(k9i|$f4$nd{ GU=6>dzh2b< literal 0 HcmV?d00001 diff --git a/python/hello-world/__pycache__/hello_world.cpython-36.pyc b/python/hello-world/__pycache__/hello_world.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9362f7be77725fb6af4a2f3ee4363087100a665 GIT binary patch literal 282 zcmXr!<>fM3)e%|3z`*brh~a=4$Z`PUVm=^|!ji(6!qmbT#gM`r%%I77i#0sIC?_SE z5hw=&Aesq?oq@QR2S}ta)-Z@Oq%esy0F^WQRk3@d=H%q-DEwknT*;uxc#9=3F*mgc zWCxh|<*J{NpPQxfnv8fY;yBcN^?@} Jz?y*~3;_1AJ!Jp@ literal 0 HcmV?d00001 diff --git a/python/hello-world/__pycache__/hello_world_test.cpython-36-PYTEST.pyc b/python/hello-world/__pycache__/hello_world_test.cpython-36-PYTEST.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4aaa3fd6e8d1b9644ffe69285d17b0ee5f94e364 GIT binary patch literal 1177 zcmbVLPjAyO6t|r;b=!3OQN@H1(kAT^3EBf!geC@J?9c;DL(0WUYmcQkNpTX!CT`uA z;XCZchd}a`6JLQ7?>TEXCMF>w`T1u%@4ese`T0q|-+lf4{NRug@`GIYe8Af<$^$Tx zNUBLjV@eS-&0^2Z{;V|%;`Nx;NddGzHG?=)-BnF z$lUVh#bjTVRXL>>VQ}_f?7%1oV7`zU=&I<9Ey#kQGVo;M)Bdi~x_Gqlt}t3|MxN~@ zWvNW{@?)K7+dINjag2wyZr&^F2G|Kt-(!XQ33bi{KT#!5EnU9aH%ViCzJV1eU=Jg3h2)F}JaBs$KTFbF zh-eKn9M)BBFeW}9%O`cJt28fdxII4+tJQr0jflP0^mtn2L!*ujyhGbrZQ1s6uM5tu zHi@wuUTuX7*9?~L1KdgAr65Do$E-dQ`kM;NpEebEhCi2UU_#1CNI;Nxcfjdy$EGPE literal 0 HcmV?d00001 diff --git a/python/hello-world/hello_world.py b/python/hello-world/hello_world.py index bfcb36f..d43fec4 100644 --- a/python/hello-world/hello_world.py +++ b/python/hello-world/hello_world.py @@ -3,5 +3,5 @@ # -def hello(name=''): - return +def hello(name='World'): + return 'Hello, '+name+'!' diff --git a/python/isogram/README.md b/python/isogram/README.md new file mode 100644 index 0000000..6412b8d --- /dev/null +++ b/python/isogram/README.md @@ -0,0 +1,30 @@ +# Isogram + +Determine if a word or phrase is an isogram. + +An isogram (also known as a "nonpattern word") is a word or phrase without a repeating letter. + +Examples of isograms: + +- lumberjacks +- background +- downstream + +The word *isograms*, however, is not an isogram, because the s repeats. + +### Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/` directory. + +For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit /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). + +## Source + +Wikipedia [https://en.wikipedia.org/wiki/Isogram](https://en.wikipedia.org/wiki/Isogram) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/python/isogram/isogram.py b/python/isogram/isogram.py new file mode 100644 index 0000000..7b0ccf9 --- /dev/null +++ b/python/isogram/isogram.py @@ -0,0 +1,2 @@ +def is_isogram(): + pass diff --git a/python/isogram/isogram_test.py b/python/isogram/isogram_test.py new file mode 100644 index 0000000..2459ae0 --- /dev/null +++ b/python/isogram/isogram_test.py @@ -0,0 +1,39 @@ +import unittest + +from isogram import is_isogram + + +# test cases adapted from `x-common//canonical-data.json` @ version: 1.1.0 + +class TestIsogram(unittest.TestCase): + + def test_empty_string(self): + self.assertTrue(is_isogram("")) + + def test_isogram_with_only_lower_case_characters(self): + self.assertTrue(is_isogram("isogram")) + + def test_word_with_one_duplicated_character(self): + self.assertFalse(is_isogram("eleven")) + + def test_longest_reported_english_isogram(self): + self.assertTrue(is_isogram("subdermatoglyphic")) + + def test_word_with_duplicated_character_in_mixed_case(self): + self.assertFalse(is_isogram("Alphabet")) + + def test_hypothetical_isogrammic_word_with_hyphen(self): + self.assertTrue(is_isogram("thumbscrew-japingly")) + + def test_isogram_with_duplicated_non_letter_character(self): + self.assertTrue(is_isogram("Hjelmqvist-Gryb-Zock-Pfund-Wax")) + + def test_made_up_name_that_is_an_isogram(self): + self.assertTrue(is_isogram("Emily Jung Schwartzkopf")) + + def test_duplicated_character_in_the_middle(self): + self.assertFalse(is_isogram("accentor")) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/leap/.cache/v/cache/lastfailed b/python/leap/.cache/v/cache/lastfailed new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/python/leap/.cache/v/cache/lastfailed @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/python/leap/README.md b/python/leap/README.md new file mode 100644 index 0000000..4bbebf3 --- /dev/null +++ b/python/leap/README.md @@ -0,0 +1,44 @@ +# Leap + +Given a year, report if it is a leap year. + +The tricky thing here is that a leap year in the Gregorian calendar occurs: + +```plain +on every year that is evenly divisible by 4 + except every year that is evenly divisible by 100 + unless the year is also evenly divisible by 400 +``` + +For example, 1997 is not a leap year, but 1996 is. 1900 is not a leap +year, but 2000 is. + +If your language provides a method in the standard library that does +this look-up, pretend it doesn't exist and implement it yourself. + +## Notes + +Though our exercise adopts some very simple rules, there is more to +learn! + +For a delightful, four minute explanation of the whole leap year +phenomenon, go watch [this youtube video][video]. + +[video]: http://www.youtube.com/watch?v=xX96xng7sAE + +### Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/` directory. + +For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit /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). + +## Source + +JavaRanch Cattle Drive, exercise 3 [http://www.javaranch.com/leap.jsp](http://www.javaranch.com/leap.jsp) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/python/leap/__pycache__/leap.cpython-36.pyc b/python/leap/__pycache__/leap.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c89f510b9cf0e74f60868af6f3f5b0b0f6117c6 GIT binary patch literal 289 zcmXr!<>lJ5x+5}(fq~&M5W@izkmUfx#XLYFg&~Epg&~R|g(;XplR23YCKN#L=8}#7*JIR=<}lK(#M{nBip#kjR_>G-xG*CSw#+ zWl<4G4ov(q*U!k$P1R2-N-E9CNiEWMNi9pw$uG!F%`4GQtw=3O&MeN=FQ_cZ$j{Ty zNlh#O6M6-ew|Fv(<3R%Pm8pqEAV(I1T*<&##0Dh&G$Ay?A_$8WD0hp)CO1E&G$+*# ItP7+G044A{8UO$Q literal 0 HcmV?d00001 diff --git a/python/leap/__pycache__/leap_test.cpython-36-PYTEST.pyc b/python/leap/__pycache__/leap_test.cpython-36-PYTEST.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7f54cf14bc686de6b43710023bb1aa702795c1b GIT binary patch literal 1323 zcmb7E&5qMB5O#j+rYT5!193wfDnV1k3UOGWl@@`-p%;2!bFtFeXkGGC>|~*cTlc`L zz|(N{1td zc$)}a&a88Hs2vzbF!UaXPh?I?i(4yr0TqFB_E>)J5YVQVXf0IvJV`+z5J2i`k-qst zln=|_2hmB9iD;^(RhkMFJrk!QEf$%`%Se0>Y9@6SEtcg;kw-|c7OVtB!^P6v!AZ@S zzHhFvMZNw zb!rsWz*SssZ{Y&4v9{X;+`SGq>>m$0hBB5XnPAKWjAaF{pcI)28GBbHY4b(FB@-2( z=%K(ouT=^*g~D=C*krg@qo5Xb@U5*FCQD?_*r0Fdw32Blb8W)I#gbjDZZYT@Y?G!q zWswh+c&}tBO!vZMI;|~$!}VPx%Frs8B`#tYTdux8LnmN3N}!DqnCgQtE~3~3-q3@F U{Zss1XyiyqNT?lpdpD@{2My9p*8l(j literal 0 HcmV?d00001 diff --git a/python/leap/leap.py b/python/leap/leap.py new file mode 100644 index 0000000..d0369a2 --- /dev/null +++ b/python/leap/leap.py @@ -0,0 +1,2 @@ +def is_leap_year(yr): + return yr % 4 == 0 and (yr % 100 != 0 or yr % 400 == 0) diff --git a/python/leap/leap_test.py b/python/leap/leap_test.py new file mode 100644 index 0000000..2b99eb8 --- /dev/null +++ b/python/leap/leap_test.py @@ -0,0 +1,23 @@ +import unittest + +from leap import is_leap_year + + +# test cases adapted from `x-common//canonical-data.json` @ version: 1.0.0 + +class YearTest(unittest.TestCase): + def test_year_not_divisible_by_4(self): + self.assertFalse(is_leap_year(2015)) + + def test_year_divisible_by_4_not_divisible_by_100(self): + self.assertTrue(is_leap_year(2016)) + + def test_year_divisible_by_100_not_divisible_by_400(self): + self.assertFalse(is_leap_year(2100)) + + def test_year_divisible_by_400(self): + self.assertTrue(is_leap_year(2000)) + + +if __name__ == '__main__': + unittest.main()