GolangでFizzBuzzしてみた
はじめに
ひょんなことから(Twitterで見た)4月にGolangの勉強会に参加することになりました。 いくら話を聴くだけとはいえ、何もわからずに参加しても楽しくないよなぁ…。
ということでちょっと触ってみることにしました。
何を作ってみるか?というところなのですが、
実は「プログラミングの基本」のような扱いを受けるFizzBuzzってやったことがなかったので、Golangでやってみることにしました。
念の為FizzBuzzのルールですが、
というルールで進めます。
インストール
Ubuntuではaptからインストールできるのですが、これは少しバージョンが古い(ver.1.6)です。
最新のver.1.8をインストールするには、下記のような方法がありそうです。
ただ、ググった中で今ひとつスタンダードなやり方が定まっていない気がしたため、1のtar.gzを使うことにしました。
インストール方法は下記を参照。
https://golang.org/doc/install
一点、/usr/localにファイルを置く下記の部分で権限エラーになったため、sudoで実行しました。
tar -C /usr/local -xzf go$VERSION.$OS-$ARCH.tar.gz
「go version」でバージョン情報が表示されたらOKです。
エディタはIntelliJ IDEAと迷いましたが、 Angularで使っていたこともありVisual Studio CodeにGolangのプラグインをインストールして使っています。
通常のFizzBuzz
ではさっそくFizzBuzzってみましょう。
まずは至って普通の方法から。
package main import "fmt" func main() { useFor() } func useFor() { for i := 1; i <= 100; i++ { if i%15 == 0 { fmt.Println("FizzBuzz") } else if i%3 == 0 { fmt.Println("Fizz") } else if i%5 == 0 { fmt.Println("Buzz") } else { fmt.Println(i) } } }
これを「go run ファイル名」で実行すればOKです。
簡単簡単。。。
}の位置にハマる
orz
✗ 間違い
if i%15 == 0 { fmt.Println("FizzBuzz") } else if i%3 == 0 { fmt.Println("Fizz") }
○ 正しい
if i%15 == 0 { fmt.Println("FizzBuzz") } else if i%3 == 0 { fmt.Println("Fizz") }
「}」と「else if{」が同じ行にないと、「'syntax error: unexpected else, expecting }‘」とエラーになります。
厳しい…
rangeを使う
さて、For文には配列を作ってrangeを使う方法もあります。
package main import "fmt" func main() { useRange() } func useRange() { for _, v := range getNumArray(1, 100) { if v%15 == 0 { fmt.Println("FizzBuzz") } else if v%3 == 0 { fmt.Println("Fizz") } else if v%5 == 0 { fmt.Println("Buzz") } else { fmt.Println(v) } } } func getNumArray(fromNum int, toNum int) []int { // fromNumからtoNumまでの配列(Slice)を作る. resultArray := make([]int, toNum-fromNum+1) for i := 0; i < len(resultArray); i++ { resultArray[i] = fromNum + i } return resultArray }
range
For文で使用し、配列(今回は[]int)を渡すことでIndexと値を取得できます。
今回は使用しませんが、Indexが必要な場合は「for _, v := 〜」の部分を「for i, v := 〜」のように変更します。
「for i := 〜」のように書くとIndexのみが取得でき、
「i」にはIndexが、「v」には値が格納されます。
「_」のように書いているのは、Goでは未使用の変数があるとエラーになってしまうためそれを避けるためです。
slice
「getNumArray」では、1から100までの数値の配列を作っています。
Rubyのように「(1..100)」とかできると楽だったのですが。
配列には「[3]int」のように要素数を指定したものと、そうでないSliceとがあります。
要素数を指定する場合、「[3]int」と「[5]int」が別の型として扱われるため、
要素を指定する必要のないSliceを使っています。
Sliceのインスタンスを初期化する場合、「make(型, 要素数)」を使うことができます。
RxGoを使う
例えばC#でFizzBuzzをする場合、Linqを使ってFor文は使わずにFizzBuzzすることができます。
しかし、残念ながら?Goには標準でそのような機能が用意されていないようです。
で、ふと思いだしたのがRxJavaで、StreamAPI的な処理ができるっていってたな〜、と。
↓
ということは、GoにもRxが用意されていれば、同じようなことできるんじゃね?
と思って調べてみたら、そのままズバリRxGoがありました。
https://github.com/ReactiveX/RxGo
そんなこんなでRxGoを使ってFizzBuzzしてみます。
go get
RxGoはGolang標準の機能ではないため、ファイルをインストールしてやる必要があります。
そのために行うのがgo getです。
READMEにある通り下記をターミナルで実行するとインストールできます。
go get -u github.com/reactivex/rxgo
今回はobservable.Range(さっきとは違うやつです)を使ってみました。
package main import ( "fmt" "github.com/reactivex/rxgo/handlers" "github.com/reactivex/rxgo/observable" ) func main() { useRx() } func useRx() { stream := observable.Range(1, 101) wait := stream.Subscribe(handlers.NextFunc(func(item interface{}) { if v, ok := item.(int); ok { if v%15 == 0 { fmt.Println("FizzBuzz") } else if v%3 == 0 { fmt.Println("Fizz") } else if v%5 == 0 { fmt.Println("Buzz") } else { fmt.Println(v) } } else { fmt.Println("Invalid value") } })) <-wait }
- 「observable.Range(1, 101)」で1から100まで(101は含まない)繰り返し実行するためのStreamを生成します。
- 「wait」の型は「channel」で、非同期で実行される処理の完了を待ちます。
- RxGoのWikiでは「handlers.NextFunc」の引数である「func(item interface{})」に型は指定されていないのですが、
少なくともver.1.8ではエラーとなるため、型を指定しています。 - 引数の型を「interface{}」とすることで、型を指定せずに引数を受け取ることができます。
- 「if v, ok := item.(int); ok {」の行で引数の型を確認し、intであった場合は「v」に格納された値を使ってFizzBuzzします。
おわりに
これまでC#やKotlinをはじめ多機能な言語ばかりを触っていたため、あれもないこれもない、おまけにフォーマットや型に厳しい(´・ω・`)
というのがGolangに対する正直な感想です。
ただ、「Goに入ってはGoに従え」という言葉もありますし、これまでの言語のやり方が完璧か?と言われればそうではない気がするので、
Golangが別の方向に進化する、というのは興味深いと思います。
一点、言語的にシンプルだったり独自路線なのは良いと思うのですが、
その先に幸せが待っている、と信じさせる何かがないと結局各々が独自にフレームワークやオレオレ実装プラグインを持ちだして大変になるのでは…?
という危惧はあります。
まぁ素人考えなので、ちゃんとその辺りも考慮して進められているのかとは思いますが。
参照
- Documentation - The Go Programming Language
- Reactive Extensions for the Go language. - ReactiveX/RxGo - GitHub
- Golang の channel の使い所 - Big Sky
- インターフェース - お気楽 Go 言語プログラミング入門
- 急いで学ぶGo lang#3 まずは基本構文 - Developers.IO
- 急いで学ぶGo lang#4 関数・ポインタ・制御構文 - Developers.IO
- 急いで学ぶGo lang#6 インターフェイス - Developers.IO
- 急いで学ぶGo lang#7 range・Array・slice・map - Developers.IO
- Go言語のchannelって一体何よ ~基礎編~【golang】 - DRYな備忘録
- interface{} な変数を型が決まっている関数の引数にする - Qiita