時代はGo*1ですね,というわけでGoやりまーす.
ところでGoという言い方とGolangっていう言い方ありますけど,どっちが正しいんでしょうか.Golangのほうがググラビリティが高いのでこちらをpreferしています.
さて,なんか新言語を学ぼうとしたとき,困るのがネタです.なにを書けばいいのか.あまりに壮大な目標を立ててしまうと難しすぎてそもそも初めての練習の役に立ちませんし,あまりに簡単だとTour of Goやれという感じで終わってしまう.微妙に実用と非実用の境目をさまよわないといけません.
こういうときにアルゴリズムに詳しかったりすると,あのアルゴリズム書いてみるか,となるのでしょうが,悲しいことにアルゴリズムマスター人材ではないので,他の陳腐なネタを探すことにします.こういうときの切り札って大事ですね.
まあそういうわけで,今回は数字をでかくするツールを作ることにします.数字をでかくするというのは粉飾決算をするツールを作るのではなく,数字を大きくしたAAを作るという意味です. 例えばこういうのです.
███ █ ███ ███ █ █ █ █ █ █ ███ █ █ █ █ █ █ █ █ █ █ ███ █ ███ █
悲しいことに文字がちゃんと表示されませんね.ちゃんと見たい方はgo get github.com/windymelt/go-exercise
ってやっると$GOPATH/bin
あたりにgo-exercise
というバイナリができるので,go-exercise 666
とかやって遊びましょう.ちなみにエラー処理とかちゃんとしていないです.そのうちやる.
実装
まず数字のフォントデータのようなものを5x3の配列で用意します.スライスじゃないのは可変じゃないからです(まなびました!!).
int
じゃなくても良い気がします.Golangにもboolってあるんでしたっけ.
var one = [5][3]int { {0, 1, 0,}, {0, 1, 0,}, {0, 1, 0,}, {0, 1, 0,}, {0, 1, 0,}, }
あとはこれをdigits
という変数に入れておきます.これも配列.
var digits = [10][5][3]int{ zero, one, two, three, four, five, six, seven, eight, nine }
あとはコマンドライン引数を1つ読んで,stringになっているのをfor rangeを使って舐めます.stringを舐めるとruneが得られる.これはUTF-8ネイティブなcharというイメージ.あとはこの文字を処理すればよい.
しかしターミナルは行単位で出力するので,5行分に分ける必要がありますね.このため工夫をして1行ずつ出力します.工夫といっても,文字単位で出力するのではなく,行単位でそれぞれの文字から1行目,2行目,などを抜き出して,その行に応じた文字を出力させます.このために,「ある数字のn行目を出力する関数」に処理を切り出しました.
func printRow(char [5][3]int, row int) { for col := 0; col <= 2; col++ { if (char[row][col] == 1) { fmt.Print("█") } else { fmt.Print(" ") } } fmt.Print(" ") }
素朴です.
さて,あとはmain
関数がよしなに行を捌いてくれます.行はかならず5行しかないので,5回ループさせれば終わりです.外側のループで1〜5行目をループさせ,内側のループで1〜n文字をループさせて扱います.
func main() { numbers := os.Args[1] for row := 0; row <= 4; row++ { for _, c := range numbers { switch c { case '0': printRow(digits[0], row) case '1': printRow(digits[1], row) case '2': printRow(digits[2], row) case '3': printRow(digits[3], row) case '4': printRow(digits[4], row) case '5': printRow(digits[5], row) case '6': printRow(digits[6], row) case '7': printRow(digits[7], row) case '8': printRow(digits[8], row) case '9': printRow(digits[9], row) default: fmt.Printf("otherwise [%c]\n", c) } } fmt.Println() } }
素朴すぎる実装ですが,まあこれでよいでしょう.runeを数値として扱うことができれば,0x30を引いて数字を得ることができるでしょう. 関数に配列のポインタを渡しているのがドエレーCOOOLですね(ここではポインタではなく参照渡しというのかな).AVR組込みでやったCを思い出します.C2019,みたいに思っておけば良いのでしょうか.あんまり言うと怒られそうなので止めておきます.俺は普段はPerlの世界で暮らしているので基本的にナチュラルに参照渡ししまくっています(そして受け取った値は変更しないのがセオリー).ところがこういう世界に来ると構造体がいるので値で渡すことも多くなります.Common Lispをやるようになってから,ある変数の中身がメモリのどこにいるのか,それは値なのか参照で渡ってきているのかを気にするようになりました.というのも,Common Lispの関数にはカジュアルに引数の中身をぶっ修正すやつがあるからです.
よかったですね.
*1:1週間のうちに言っていることが三転していておもしろい