From a5831193a8316d077cf9d187f7d18a8fd8442688 Mon Sep 17 00:00:00 2001 From: Ryan Cavicchioni Date: Sun, 16 Feb 2020 14:49:53 -0600 Subject: [PATCH] last exercises --- exercise-equivalent-binary-trees.go | 55 +++++++++++++++ exercise-web-crawler.go | 106 ++++++++++++++++++++++++++++ mutex-counter.go | 39 ++++++++++ 3 files changed, 200 insertions(+) create mode 100644 exercise-equivalent-binary-trees.go create mode 100644 exercise-web-crawler.go create mode 100644 mutex-counter.go diff --git a/exercise-equivalent-binary-trees.go b/exercise-equivalent-binary-trees.go new file mode 100644 index 0000000..02a991b --- /dev/null +++ b/exercise-equivalent-binary-trees.go @@ -0,0 +1,55 @@ +package main + +import "golang.org/x/tour/tree" + +import ( + "fmt" + "sort" +) + +// Walk walks the tree t sending all values +// from the tree to the channel ch. +func Walk(t *tree.Tree, ch chan int) { + if t.Value > 0 { + ch <- t.Value + } + if t.Left != nil { + go Walk(t.Left, ch) + } + if t.Right != nil { + go Walk(t.Right, ch) + } +} + +// Same determines whether the trees +// t1 and t2 contain the same values. +func Same(t1, t2 *tree.Tree) bool { + s1 := make([]int, 10) + s2 := make([]int, 10) + ch1 := make(chan int) + ch2 := make(chan int) + go Walk(t1, ch1) + go Walk(t2, ch2) + for i := 0; i < 10; i++ { + s1 = append(s1, <- ch1) + s2 = append(s2, <- ch2) + } + sort.Ints(s1) + sort.Ints(s2) + for i, _ := range s1 { + if s1[i] != s2[i] { + return false + } + } + return true +} + +func main() { + ch := make(chan int) + go Walk(tree.New(1), ch) + for i := 0; i < 10; i++ { + fmt.Println(<- ch) + } + fmt.Println(Same(tree.New(1), tree.New(1))) + fmt.Println(Same(tree.New(1), tree.New(2))) +} diff --git a/exercise-web-crawler.go b/exercise-web-crawler.go new file mode 100644 index 0000000..74e390f --- /dev/null +++ b/exercise-web-crawler.go @@ -0,0 +1,106 @@ +package main + +import ( + "fmt" + "sync" +) + +type SafeCounter struct { + v map[string]int + mux sync.Mutex +} + +func (c *SafeCounter) Inc(key string) { + c.mux.Lock() + defer c.mux.Unlock() + c.v[key]++ +} + +func (c *SafeCounter) Value(key string) int { + c.mux.Lock() + defer c.mux.Unlock() + return c.v[key] +} + +type Fetcher interface { + // Fetch returns the body of URL and + // a slice of URLs found on that page. + Fetch(url string) (body string, urls []string, err error) +} + +// Crawl uses fetcher to recursively crawl +// pages starting with url, to maximum of depth. +func Crawl(url string, depth int, fetcher Fetcher, c SafeCounter) { + // TODO: Fetch URLs in parallel. + // TODO: Don't fetch the same URL twice. + // This implementation doesn't do either. + if depth <= 0 { + return + } + if c.Value(url) > 0 { + return + } + body, urls, err := fetcher.Fetch(url) + if err != nil { + fmt.Println(err) + return + } + fmt.Printf("found: %s %q\n", url, body) + for _, u := range urls { + go Crawl(u, depth-1, fetcher, c) + } +} + +func main() { + c := SafeCounter{v: make(map[string]int)} + Crawl("https://www.golang.com/", 4, fetcher, c) +} + +type fakeFetcher map[string]*fakeResult + +// fakeFetcher is Fetcher that returns canned results. +type fakeResult struct { + body string + urls []string +} + +func (f fakeFetcher) Fetch(url string) (string, []string, error) { + if res, ok := f[url]; ok { + return res.body, res.urls, nil + } + return "", nil, fmt.Errorf("not found: %s", url) +} + +// fetcher is a populated fakeFetcher. +var fetcher = fakeFetcher{ + "https://golang.org/": &fakeResult{ + "The Go Programming Language", + []string{ + "https://golang.org/pkg/", + "https://golang.org/cmd/", + }, + }, + "https://golang.org/pkg/": &fakeResult{ + "Packages", + []string{ + "https://golang.org/", + "https://golang.org/cmd/", + "https://golang.org/pkg/fmt/", + "https://golang.org/pkg/os/", + }, + }, + "https://golang.org/pkg/fmt/": &fakeResult{ + "Package fmt", + []string{ + "https://golang.org/", + "https://golang.org/pkg/", + }, + }, + "https://golang.org/pkg/os/": &fakeResult{ + "Package os", + []string{ + "https://golang.org/", + "https://golang.org/pkg/", + }, + }, +} diff --git a/mutex-counter.go b/mutex-counter.go new file mode 100644 index 0000000..1f7aec9 --- /dev/null +++ b/mutex-counter.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "sync" + "time" +) + +// SafeCounter is safe to use concurrently +type SafeCounter struct { + v map[string]int + mux sync.Mutex +} + +// Inc increments the coutner for the given key +func (c *SafeCounter) Inc(key string) { + c.mux.Lock() + // Lock so only one goroutine at a time can access the map c.v + c.v[key]++ + c.mux.Unlock() +} + +// Value returns the current value of the counter for the given key +func (c *SafeCounter) Value(key string) int { + c.mux.Lock() + // Lock so only one goroutine at a time can access the map c.v + defer c.mux.Unlock() + return c.v[key] +} + +func main() { + c := SafeCounter{v: make(map[string]int)} + for i := 0; i < 1000; i++ { + go c.Inc("somekey") + } + + time.Sleep(time.Second) + fmt.Println(c.Value("somekey")) +}