Goで遊んでみた

ググりやすさが著しく悪いプログラミング言語と言ったら僕の中ではGoとIoです。

それにしても最近Goの名前をよく聞くようになりました。発表されたころにコードを少し見たことがありますが、「なんでチャンネルを”chan”って書くんだよ!」って突っ込んだきりで、それ以降は特に注目していなかったのが正直なところです。しかしGo製のプロダクトは増えてきています。Dockerとか。なんだかAndroidアプリも書けるようにしてきていますし。

ということで少しだけGoで遊んでみました。とりあえず先にコードを載せておきます。

package main

import (
  "fmt"
  "strconv"
  "strings"
  "time"
)

func main() {
  nozomi := make(chan string)
  nico   := make(chan string)

  go GenerateNozomiMessage((chan<- string)(nozomi), 9)
  go GenerateNicoMessage((chan<- string)(nico), 2)

  for {
    select {
    case message := <-nozomi:
      fmt.Println(message)
    case message := <-nico:
      fmt.Println(message)
    }
  }
}

func GenerateNozomiMessage(ch chan<- string, people int64) {
  ch <- "うちを入れて" + strconv.FormatInt(people, 10) + "人や"
  time.Sleep(777 * time.Millisecond)
  GenerateNozomiMessage(ch, people + 1)
}

func GenerateNicoMessage(ch chan<- string, times int) {
  ch <- strings.Repeat("にっこ", times) + "にー♪"
  time.Sleep(1000 * time.Millisecond)
  GenerateNicoMessage(ch, times + 1)
}

このコードはGoをインストール(OS Xなら”$ brew install go”)し、”$ go run 上記コードを保存したファイルのパス”とすれば動作します。

このプログラムはチャンネルを使ってみることを目的に書いています。なぜならGoと言えばチャンネルだからです(僕の中では)。

軽く説明しますと:

nozomi := make(chan string)
nico   := make(chan string)

でnozomiチャンネルとnicoチャンネルというふたつのチャンネルを作成しています。チャンネルとは名前付きパイプみたいなものだと思ってください。今回は”chan string”としているので、文字列を渡したり受け取ったり出来るチャンネルになります。

go GenerateNozomiMessage((chan<- string)(nozomi), 9)
go GenerateNicoMessage((chan<- string)(nico), 2)

作成したチャンネルを出力専用にキャストして、GenerateNozomiMessage関数とGenerateNicoMessage関数に渡して呼び出します。ここで付与している”go”はこの関数呼び出しをGoroutineとするものです。Goroutineってナニソレって感じですが、スレッドみたいなもんだと思えば大丈夫だと思います。つまり、”go”を付与すると、GenerateNozomiMessage関数はGoroutineとして別のところで実行され、すぐさま次の処理(GenerateNicoMessage関数の呼び出し)に移ります。「関数の結果はどうやって受け取るんだよ」って感じですが、そのためのチャンネルです。

for {
  select {
  case message := <-nozomi:
    fmt.Println(message)
  case message := <-nico:
    fmt.Println(message)
  }
}

チャンネルに出力するのは”チャンネル名 <- 値"と言う風にします(28行目、34行目)。チャンネルから値を取り出す(入力)のは"<-チャンネル名"です。ただ今回のように複数のチャンネルから同時に取り出したい場合はselectを用います。これでnozomiチャンネルとnicoチャンネル、どちらかから値を取り出せるときに対応したcaseの中が実行されます。forでselectを延々と実行しているので、nozomiチャンネルは(狂ったように)"うちを入れて82,421人や"と言いますし、nicoチャンネルは(狂ったように)"にっこにっこにっこにっこにっこにっこにっこにっこにっこにっこにっこにっこにっこにっこにー♪"と言います。これらはtime.Sleepする量をズラしているので、ちょっとずつタイミングがズレて表示されます。

今回はただ一定時間スリープして文字列を出力しているだけですが、外部RESTful APIを同時に叩いて結果を取得出来たものから順に処理していくクライアントを書いたり、HTTPリクエストを受け付けながらそのHTTPリクエストを処理するワーカーからの「HTTPリクエスト処理出来るからちょうだい」という要求(pullのことです)を受け付けてHTTPリクエストをワーカーにアサインするサーバー(まんまunicornです)を書いたりと、いろいろ出来そうです。

僕はErlangが好きで、ちょっと前は結構書いていたんですよ。なので、チャンネルのこともアクターモデルと言われればすぐ分かりました。ただそれと同時にチャンネルという概念は不要じゃないかな、とも思いました。Erlangのアクター(軽量プロセス)が十分分かりやすかったので。

でも触ってみた感想としては「ええやん」って感じですね。チャンネルはメールボックスのようなものだと思っていますが、それがコードとして目に見えるし、その上でチャンネルはファーストクラスなので関数に受け渡したり出来て柔軟です(ErlangもプロセスIDはファーストクラスですが)。

やっぱり「なんでchanなんだ……」と思いもしますが、結構面白そうなプログラミング言語なので、今後注目していこうと思います。

コニョル

ググるときは"go lang"で

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>