コミュ障だから明日が僕らをよんだって返事もろくにしなかった

何かを創る人に憧れたからブログをはじめたんだと思うよ

Goツアーの思い出 Part 3

これは僕が業に触れた話

この世界を繋ぐあらゆる理は、阿頼耶識の生み出した白昼夢なのかもしれない。最近、僕が僕であるために生きるということ自体が苦楽を含んだ報いなのだと思うようになった。それで生活が変わるわけでもないが……。



という妄想をしていたら夜になっていました。

Go Gou Golang

ということで、妄想だけ綴ってもアレですしGo言語の話をします。

前回記事
inujini.hatenablog.com


ということで、Go言語の特徴の一つである「Concurrency」をやっていきます。聞きなれない単語ですが「並行性」とか訳される単語です。意味合いとしては処理の状態を複数保っている感じでしょうか。時間軸で示すと、同時に実行しているわけでなくただ実行のタスクを保持して、終了したら次をさばくみたいなイメージです。


1. Goroutines
ゴルーチンという謎用語が出てきますが、Go言語の生み出したコルーチン*1的なやつです。

実行するにはこれでいけるらしいです。

go 関数( )
func say(s string) {
	for i := 0; i < 5; i++ {
		time.Sleep(100 * time.Millisecond)
		fmt.Println(s)
	}
}

func main() {
	go say("赤あげて")
	go say("白あげないで")
	go say("赤あげないで")
	go say("白あげて")
	say(" ")
}

処理を担保しているけども、別に交互に実行するわけでもない(※実行順は担保されない)のでランダム性がうまれてなんかゲームっぽい挙動になる。


2. Channels
ゴルーチン間の連携をつかさどるチャネルについて、チャネルオペレータの<-を用いて値の送受信ができるようです。
・チャネルの受信

ch <- v    // v をチャネル ch へ送信する
v := <-ch  // ch から受信した変数を v へ割り当てる

・チャネルの作成

ch := make(chan int)


3. Buffered Channels

チャネルを作るときに第二引数をいじるとバッファ上限をいじれるようです。

ch := make(chan int, バッファの長さ)

バッファが詰まるよう例文をいじってみるとこんな感じで怒られる。

fatal error: all goroutines are asleep - deadlock!


4. Range and Close
チャネルのクローズについての確認

v, ok := <-ch

チャネルのクローズ

close(ch)



5. Select
6. Default Selection
selectを使うことで複数の通信操作ができるようになる。どのcaseも準備できていない場合はデフォルトに行くそうです。

select {
case i := <-c:
    // use i
default:
    // receiving from c would block
}
}

サンプル

func main() {
	tick := time.Tick(100 * time.Millisecond)
	boom := time.After(500 * time.Millisecond)
	for {
		select {
		case <-tick:
			fmt.Println("tick.")
		case <-boom:
			fmt.Println("BOOM!")
			return
		default:
			fmt.Println("    .")
			time.Sleep(50 * time.Millisecond)
		}
	}
}



7-8. Exercise: Equivalent Binary Trees
突然の実習問題!! 二分木が同じ値を保持するか問題

package main

import "code.google.com/p/go-tour/tree"
import "fmt"

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int){
    var walk func(*tree.Tree)
    walk = func(t *tree.Tree) {
        if t != nil {
            walk(t.Left)
            ch <- t.Value
            walk(t.Right)
        }
    }
    walk(t)
    close(ch)
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
    c1,c2 := make(chan int), make(chan int)
    go Walk(t1, c1)
    go Walk(t2, c2)
 
    for {
        n1, ok1 := <-c1
        n2, ok2 := <-c2
        if n1 != n2 || ok1 != ok2 {
            return false
        }
        if !ok1 {
            break
        }
    }
    return true
}

func main() {
    fmt.Println(Same(tree.New(1), tree.New(1)))
    fmt.Println(Same(tree.New(1), tree.New(2)))
}

多分こんな感じ。マジで自信ない。Treeパッケージが強すぎてどうにでもなる気がして、うまく動いている気が全然しない。



9.sync.Mutex
syncパッケージを利用することで、排他ロックが効くそうです。
sync - The Go Programming Language


10. Exercise: Web Crawler
突然の実習問題!!ウェブクローラを並"列"化するお。

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)
}

// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher) {
    // TODO: Fetch URLs in parallel.
    // TODO: Don't fetch the same URL twice.
    // This implementation doesn't do either:
    isFetched := make(map[string]bool)
    crawl(url, depth, fetcher, isFetched)
    return
}

func crawl(url string, depth int, fetcher Fetcher, isFetched map[string]bool) {
    if depth <= 0 {
        return
    }
    if _, ok := isFetched[url]; ok {
        return
    }

    body, urls, err := fetcher.Fetch(url)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("found: %s %q\n", url, body)
    isFetched[url] = true
    // WaitGroup
    var wg sync.WaitGroup
    for _, u := range urls {
        wg.Add(1)
        go func(u string) {
            defer wg.Done()
            crawl(u, depth-1, fetcher, isFetched)
        }(u)
    }
    wg.Wait()
	
    return
}

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/",
		},
	},
}

多分こんな感じ、全然わからん。WaitGroupを使うことで良い感じになるらしいということがググってみてわかった。


おわった

かなり理解が怪しい部分があるけど終わらせた。とりあえず演習問題難しかった。分からんとかやりながら書き直すたびにコードがどんどん短くなっていっていったのはGoっぽい書き方ができるようになってきたからなのか……。まあ、いいや。これにてGoツアー終了です。



*1: プログラミングの構造の一種。サブルーチンがエントリーからリターンまでを一つの処理単位とするのに対し、コルーチンはいったん処理を中断した後、続きから処理を再開できる。接頭辞 co は協調を意味するが、複数のコルーチンが中断・継続により協調動作を行うことによる。[Wikiより]