CopyPastor

Detecting plagiarism made easy.

Score: 0.800081193447113; Reported for: String similarity Open both answers

Possible Plagiarism

Plagiarized on 2023-11-04
by Nick K

Original Post

Original - Posted on 2019-07-04
by Giulio Micheloni



            
Present in both answers; Present only in the new answer; Present only in the old answer;

Just started learning Go and here is my solution. I hope that it is simple, in a spirit of "A Tour of Go" and not using things that are not in "Tour" like `WaitGroup`. ([Playground][1])
package main import ( "fmt" "sync" ) type Fetcher interface { // Fetch returns the body of URL and // a slice of URLs found on that page. Fetch(url string, cache *safeCache) (body string, urls []string, err error) } // Crawl uses fetcher to recursively crawl // pages starting with url, to a maximum of depth. func Crawl(url string, depth int, fetcher Fetcher, result chan string, cache *safeCache) { defer close(result) if depth <= 0 { return } body, urls, err := fetcher.Fetch(url, cache) if err != nil { result <- err.Error() return } result <- fmt.Sprintf("found: %s %q", url, body) _result := make([]chan string, len(urls)) for i, u := range urls { _result[i] = make(chan string) go Crawl(u, depth-1, fetcher, _result[i], cache) } for i := range _result { for s := range _result[i] { result <- s } } } func main() { entry := "https://golang.org/" depth := 4 result := make(chan string) cache := &safeCache{visited: make(map[string]bool)} go Crawl(entry, depth, fetcher, result, cache) for s := range result { fmt.Println(s) } } // fakeFetcher is Fetcher that returns canned results. type fakeFetcher map[string]*fakeResult type fakeResult struct { body string urls []string } type safeCache struct { mu sync.Mutex visited map[string]bool } func (c *safeCache) Visit(url string) { c.mu.Lock() c.visited[url] = true c.mu.Unlock() } func (c *safeCache) IsVisited(url string) bool { c.mu.Lock() defer c.mu.Unlock() return c.visited[url] } func (f fakeFetcher) Fetch(url string, cache *safeCache) (body string, urls []string, err error) { if cache.IsVisited(url) { return "", nil, fmt.Errorf("visited: %s", url) } if res, ok := f[url]; ok { cache.Visit(url) 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/", }, }, }

[1]: https://go.dev/play/p/ygzlPK3oPmP
The main issue is that you forgot to release the mutex before returning in the `if isCrawled {}` branch.
Moreover, I would suggest to use synchronization APIs if you actually need to synchronize goroutines. Channels are better used for communicating and sharing data.
This is the solution with `sync.WaitGroup`: https://play.golang.org/p/slrnmr3sPrs
Here is instead you solution with only channels: https://play.golang.org/p/FbPXxPSXvFL
The problem was that the very first time you call `CrawlURL()` you are not reading from the channel you pass as argument. Therefore, once that function tries to send something into it through `defer func() { quit <- true }()`, it block forever and never returns.
package main import ( "fmt" "sync" ) 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) } var crawledURLs = make(map[string]bool) var mux sync.Mutex func CrawlURL(url string, depth int, fetcher Fetcher, quit chan bool) { //For very first function instance, this would block forever if //nobody is receiving from the other end of this channel defer func() { quit <- true }() if depth <= 0 { return } mux.Lock() _, isCrawled := crawledURLs[url] if isCrawled { mux.Unlock() return } crawledURLs[url] = true mux.Unlock() body, urls, err := fetcher.Fetch(url) if err != nil { fmt.Println(err) return } fmt.Printf("found: %s %q\n", url, body) quitThis := make(chan bool) for _, u := range urls { go CrawlURL(u, depth-1, fetcher, quitThis) } for range urls { <-quitThis } return } // Crawl uses fetcher to recursively crawl // pages starting with url, to a maximum of depth. func Crawl(url string, depth int, fetcher Fetcher) { lastQuit := make(chan bool) go CrawlURL(url, depth, fetcher, lastQuit) //You need to receive from this channel in order to //unblock the called function <-lastQuit return } func main() { Crawl("https://golang.org/", 10, fetcher) } // fakeFetcher is Fetcher that returns canned results. type fakeFetcher map[string]*fakeResult 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/", }, }, }


        
Present in both answers; Present only in the new answer; Present only in the old answer;