2017-08-15
This commit is contained in:
parent
549ba5084e
commit
4a9b690bb2
53
go/accumulate/README.md
Normal file
53
go/accumulate/README.md
Normal file
@ -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.
|
11
go/accumulate/accumulate.go
Normal file
11
go/accumulate/accumulate.go
Normal file
@ -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
|
||||||
|
}
|
57
go/accumulate/accumulate_test.go
Normal file
57
go/accumulate/accumulate_test.go
Normal file
@ -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()
|
||||||
|
}
|
||||||
|
}
|
31
go/error-handling/README.md
Normal file
31
go/error-handling/README.md
Normal file
@ -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.
|
57
go/error-handling/common.go
Normal file
57
go/error-handling/common.go
Normal file
@ -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)
|
35
go/error-handling/error_handling.go
Normal file
35
go/error-handling/error_handling.go
Normal file
@ -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
|
||||||
|
}
|
191
go/error-handling/error_handling_test.go
Normal file
191
go/error-handling/error_handling_test.go
Normal file
@ -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")
|
||||||
|
}
|
||||||
|
}
|
@ -1,26 +1,109 @@
|
|||||||
package paasio
|
package paasio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
const testVersion = 3
|
const testVersion = 3
|
||||||
|
|
||||||
func NewWriteCounter() WriteCounter {
|
func NewReadCounter(r io.Reader) ReadCounter {
|
||||||
w := new(WriteCounter)
|
return &readCounter{
|
||||||
return w
|
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
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReadCounter() ReadCounter {
|
func (nw *nopWriter) WriteCount() (n int64, ops int) {
|
||||||
r := new(ReadCounter)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReadCounter) ReadCount() (int64, int) {
|
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReadWriteCounter() *ReadWriteCounter {
|
|
||||||
r := new(ReadWriteCounter)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
59
go/pov/README.md
Normal file
59
go/pov/README.md
Normal 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
122
go/pov/pov.go
Normal 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
295
go/pov/pov_test.go
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1 +0,0 @@
|
|||||||
example
|
|
BIN
python/example/__pycache__/example.cpython-36.pyc
Normal file
BIN
python/example/__pycache__/example.cpython-36.pyc
Normal file
Binary file not shown.
BIN
python/example/__pycache__/example_test.cpython-36-PYTEST.pyc
Normal file
BIN
python/example/__pycache__/example_test.cpython-36-PYTEST.pyc
Normal file
Binary file not shown.
1
python/hello-world/.cache/v/cache/lastfailed
vendored
Normal file
1
python/hello-world/.cache/v/cache/lastfailed
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
BIN
python/hello-world/__pycache__/hello_world.cpython-36-PYTEST.pyc
Normal file
BIN
python/hello-world/__pycache__/hello_world.cpython-36-PYTEST.pyc
Normal file
Binary file not shown.
BIN
python/hello-world/__pycache__/hello_world.cpython-36.pyc
Normal file
BIN
python/hello-world/__pycache__/hello_world.cpython-36.pyc
Normal file
Binary file not shown.
Binary file not shown.
@ -3,5 +3,5 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
def hello(name=''):
|
def hello(name='World'):
|
||||||
return
|
return 'Hello, '+name+'!'
|
||||||
|
30
python/isogram/README.md
Normal file
30
python/isogram/README.md
Normal file
@ -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/<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).
|
||||||
|
|
||||||
|
## 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.
|
2
python/isogram/isogram.py
Normal file
2
python/isogram/isogram.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
def is_isogram():
|
||||||
|
pass
|
39
python/isogram/isogram_test.py
Normal file
39
python/isogram/isogram_test.py
Normal file
@ -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()
|
1
python/leap/.cache/v/cache/lastfailed
vendored
Normal file
1
python/leap/.cache/v/cache/lastfailed
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
44
python/leap/README.md
Normal file
44
python/leap/README.md
Normal file
@ -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/<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).
|
||||||
|
|
||||||
|
## 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.
|
BIN
python/leap/__pycache__/leap.cpython-36.pyc
Normal file
BIN
python/leap/__pycache__/leap.cpython-36.pyc
Normal file
Binary file not shown.
BIN
python/leap/__pycache__/leap_test.cpython-36-PYTEST.pyc
Normal file
BIN
python/leap/__pycache__/leap_test.cpython-36-PYTEST.pyc
Normal file
Binary file not shown.
2
python/leap/leap.py
Normal file
2
python/leap/leap.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
def is_leap_year(yr):
|
||||||
|
return yr % 4 == 0 and (yr % 100 != 0 or yr % 400 == 0)
|
23
python/leap/leap_test.py
Normal file
23
python/leap/leap_test.py
Normal file
@ -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()
|
Loading…
Reference in New Issue
Block a user