A Tour of Goの 71番。まだ goroutineと仲良くなれない。とりあえず動いた版が以下の通りだが、FetchResultに urlが入っているのが冗長だったりと無駄が残っている。試行錯誤の過程ということで
これでgolangは 2周。リファレンスを引きながらコードを書ける程度にはなった。
package main import ( "fmt" ) type FetchResult struct { url string body string } type FetchCache map[string]FetchResult 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 a maximum of depth. func Crawl(url string, depth int, fetcher Fetcher) { done := make(chan bool) cache := make(FetchCache) count := 1 go Crawl0(url, depth, fetcher, done, &count, &cache) for <-done { count-- if count == 0 { break } } } func Crawl0(url string, depth int, fetcher Fetcher, done chan bool, count *int, cache *FetchCache) { // Don't fetch the same URL twice. if _, ok := (*cache)[url]; ok == true { done <- true return } if depth <= 0 { done <- true return } body, urls, err := fetcher.Fetch(url) if err != nil { fmt.Println(err) done <- true return } fmt.Printf("found: %s %q\n", url, body) (*cache)[url] = FetchResult{url, body} for _, u := range urls { // Fetch URLs in parallel. *count = *count + 1 go Crawl0(u, depth-1, fetcher, done, count, cache) } done <- true } func main() { Crawl("http://golang.org/", 4, 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{ "http://golang.org/": &fakeResult{ "The Go Programming Language", []string{ "http://golang.org/pkg/", "http://golang.org/cmd/", }, }, "http://golang.org/pkg/": &fakeResult{ "Packages", []string{ "http://golang.org/", "http://golang.org/cmd/", "http://golang.org/pkg/fmt/", "http://golang.org/pkg/os/", }, }, "http://golang.org/pkg/fmt/": &fakeResult{ "Package fmt", []string{ "http://golang.org/", "http://golang.org/pkg/", }, }, "http://golang.org/pkg/os/": &fakeResult{ "Package os", []string{ "http://golang.org/", "http://golang.org/pkg/", }, }, }