2014年1月31日金曜日

Golang Cafe #14を開催しました。

Golang Cafe #14を開催しました。今回は、flagパッケージの使い方を見ていきました。
サンプルコードは少ないですが、githubにありますので、御覧ください。

ポイントを紹介すると、goでは、コマンド引数を受け取るためには、flag.Parse()を呼びださなければいけません。この時、引数の解析が行われます。
https://github.com/tyokoyama/golangcafe/blob/master/sample.com/flagsample/flagsample.go#L33

引数のリストをただ取得するだけなら、flag.Args()か、flag.Arg(i)
flag.Args()は[]stringに全ての引数が文字列として格納されます。ただし、オプションは含まれません。ということで、Goでは、オプションの解析はランタイム側でやってくれるというので便利に使えます。
https://github.com/tyokoyama/golangcafe/blob/master/sample.com/flagsample/flagsample.go#L37

オプションを指定する場合、例えば、flag.Bool(オプション名,既定値,ヘルプメッセージ)を呼び出し、受け入れるオプションを指定します。flag.Bool()だけでなく、flag.Int()や、flag.Float64()などもあります。
https://github.com/tyokoyama/golangcafe/blob/master/sample.com/flagsample/flagsample.go#L25

Durationを指定する場合は、"-time=1s"など、timeパッケージで指定するような文字列を指定することになります。
また、整数型などで、"-i 10"とすると、-iオプションに10を指定するという意味になりますが、"-i hoge"とやると、flag.Parse()の段階でエラー終了するので、指定方法は注意して下さい。

オプションの指定内容を任意の変数に渡す場合は、flag.BoolVar()を呼び出して下さい。
これもInt、Floatなど、同じようなメソッドはたくさんあります。
https://github.com/tyokoyama/golangcafe/blob/master/sample.com/flagsample/flagsample.go#L30

flag.PrintDefaults()を呼び出すと、ヘルプメッセージと既定値が出力されます。
コマンド引数が不正な時や、ヘルプ機能を実装する時に利用すると便利です。
https://github.com/tyokoyama/golangcafe/blob/master/sample.com/flagsample/flagsample.go#L41

最後に、オプション引数を全てチェックしたい場合には、flag.Visit()があります。flag.Visit()とflag.VisitAll()がありますが、Visit()の方は、指定されたもののみが、引数として呼び出され、VisitAll()は指定されていないものも全て引数に指定されて呼び出されます。
https://github.com/tyokoyama/golangcafe/blob/master/sample.com/flagsample/flagsample.go#L46

これで、flagパッケージの使い方は大体分かった…
のですが、「オプション引数の順番を解析する方法は?」という疑問が上がったので、
その時、「順番は解析できないだろう」と(C言語とかだと、引数が文字列として渡されるのでチェック可能だろうけど、力づくよね…)言っていたのですが、実際に動作している
godocコマンドや、goコマンドのソースコードを読み進めてみました。

私は、goコマンドしか読み進めなかったので、goコマンドの引数のチェックについて書き残しておきます。

goコマンドは例えば、
$ go test -v

という感じでコマンドを実行します。最初にflag.Parse()が実行されるので、全て文字列の引数として受け取ります。

ここで、goコマンドは賢く振舞っていました。flagパッケージには、FlagSet型のflagを再構築(?)する構造体があり、第1引数でサブコマンドに分類した後、第2引数以降を再度FlagSetを使って解析していました。
http://golang.org/pkg/flag/#FlagSet

と言ったところで、今回は終了しました。
goコマンドで使えないオプションを指定した時に、エラーにしている所があったので、探していたのですが、結局見つけられず…。flagパッケージのソースコードに埋め込まれたメッセージが表示されていたので、どこかでParse()しているはずなんですが…。

ということで、goコマンド自体は、かなり読みやすいコードだと思いました。コマンドの分類もしてありますし、構成もシンプルでわかりやすいものでしたので、一度、読んでみるのもいいのではないでしょうか。

次回は、netパッケージになります。

2014年1月24日金曜日

Golang Cafe #13を開催しました。

Golang Cafe #13を開催しました。

今回は、containerパッケージを見ていきました。サンプルはgithubにありますので、ごらんください。

まず、container/heapパッケージの使い方ですが、heap.Interfaceという独自のinterfaceを実装する必要があります。

http://golang.org/pkg/container/heap/#Interface
http://golang.org/pkg/sort/#Interface

heap.Interfaceはsort.InterfaceのCompositeですから、メソッドを5個実装しなければいけません。結構面倒です。ですが、データ構造がプログラマが自由に決定するようになっていますから、プログラマでその辺りは、管理しなければいけないようです。

以下のメソッドはsort.Interfaceから継承したものです。
Len() int
ヒープの長さ(データ量)を返す

Less(i, j int) bool
大小関係をチェックする(i番目とj番目の比較。i > jの条件にすると大きい順にすることもできる。)

Swap(i, j int)
データの入れ替え(i番目とj番目)を入れ替える。

残りは、heap特有のインターフェイスです。
Push(x interface{})
heapにデータを追加します。

Pop() interface{}
heapからデータを取り出します。

intafaceを実装したら、最初にヒープの初期化を行います。heap.Init()を呼び出します。
実装例はheapsample.goにあります。今回は、UnitTestを行うので、パッケージの初期化時にいれてみました。

データの追加方法は、
heap.Push(golangcafeheap, member)
のように行います。第1引数は、heap.Init()で指定した、heap.Interfaceを実装したインスタンスを指定します。heapパッケージのPushを呼び出すと、Push後に並び替え処理が実行されるようになります。

データの取り出しは
heap.Pop(golangcafeheap)
のように行います。引数はPushの時と同じものを指定します。heap.Pushと同じようにPop()時も並び替えが行われます。

データの削除はheap.Remove()を呼び出せば削除されます。
heap.Fix()は、何が起こるのかよくわかりませんでしたが、heapの動きがしっかり理解できていれば意味がわかるのかもしれません…。

heapパッケージのサンプルを作るのに苦戦してしまって、ringのサンプルが間に合いませんでした…。

container/listパッケージですが、
PushBack() 末尾に追加
PushFront() 先頭に追加
InsertAfter() ある要素の次に追加
InsertBefore() ある要素の前に追加
を試しました。これは、単純なLinked Listですから、構造も複雑ではないので、難しく無いと思います。他にも関数はありますが、今回は実行しませんでした。
サンプルコードはlistsample_test.goです。

container/ringパッケージは、リングバッファを実現するためのパッケージです。
循環するので、終了条件が面倒に感じましたが、「全てのオブジェクトに対して、何か処理をする」という要求に答えるのには、便利なものなのかな?と思いました。
サンプルコードは、ringsample_test.goです。

ただ、サンプルコードが間に合わず、GolangCafeで集まった時にサンプルを動かしていたのですが、ポインタとType Assertionの組み合わせでハマってしまい、動くコードになるまで時間がかかってしまいました。ポインタに対しても、正しい型を指定してあげないと、Type Assertionはfalseを返してくるのでC言語と同じようにプログラマが型を正しく把握していないとハマってしまう原因になるので注意が必要です。

https://plus.google.com/114183076079015753160/posts/jCXqzMiaC2u

次回は、flagパッケージを見ていく予定です。

2014年1月13日月曜日

Golang Cafe #12を開催しました。

Golang Cafe #12を開催しました。
用意したサンプルコードは、githubにおいてありますので、ごらんください。

今回は、いつも3人のところ、四国からも参加があり、4人での回になりました。
(毎回新しく参加される方がいなかったので、3人揃った時点で監視をやめてしまいました。次回からアラームをつけておこうと思います)

今回は、compressパッケージを見ていくということで、bzip2/flate/gzipの3つのサンプルを動かしました。他にも、lzwとzlibパッケージもありますが、ほぼ同じコードになってしまうことと、実際の用途が思いつかなかったので、現時点でよく利用されている3つのパッケージのサンプルを用意しました。

まず、bzipパッケージですが、展開はできても、圧縮のAPIがないため、圧縮はできません。
単体のファイルを圧縮するサンプルがbzip.goです。テキストファイルをbzip2に圧縮したファイルを展開しています。圧縮/展開と言っても、Readerを生成して、データを読み取るだけのロジックで、非常にシンプルにできています。ファイル単体なら、最小で3行程度で展開できてしまいます。

では、複数のファイルの場合はどうするかというと、tarbzip.goのように、前回でてきた、archive/tarパッケージを使って、bzipのReaderからtarのReaderを生成し、前回のサンプルと全く同じ方法で読み出すだけとなります。
compressパッケージのReaderはDecoratorパターンのような記述が可能です。

flateパッケージは、圧縮/展開のAPI両方が用意されています。まず、圧縮のflate.goですが、flate.NewWriter()を呼び出すと、flateのWriterが生成されます。Writerが圧縮処理を担当するので、呼び出し側は、何も考えずに圧縮対象のbyteデータをWriteするだけ。となっています。Golang Cafeの前に用意した時は、byte.Bufferに一度書き込みしてから、flateのWriterに書き込みをしていましたが、io.Writerを実装している(Write([]byte)がある)のでそのままデコレータパターンで使うことができます。
同様に、readflate.goも同様です。io.ReadCloserを実装していますが、io.ReadCloserは、io.Readerとio.Closer()のCompositeなので、そのまま使うことができます。
が、io.Closer()が付いているということは、Close()があるので、サンプルコードでは何も起きませんでしたが、実際には、Close()を呼び出すために、一度変数に入れておいたほうがいいかもしれません。(flateパッケージのソースコードを呼ぶ限りだと、Close()で何もしておらず、Read時にエラーがあれば、それが返されるようです)

gzipパッケージもflateがgzipになっただけなので、gzip.goreadgzip.goをごらんください。

最終的に、最速ペースで進んでいたのですが、説明の途中に、「インターフェイスが実装されているかどうかを判断する方法が難しい」という話題になり、


確かに、Go言語は「メソッドが実装されていれば、インターフェイスを実装している」と扱われるため、実際には実装されている(今回のパターンだと、Write([]byte)が実装されているので、io.Writerとして扱える)が、プログラマの誤解により、「型が違うから別のもの」と判断してしまうことがあります。これを何とか回避する方法を考えていると

Type Assertionを使って判定する方法がある。というヒントを得ました。
いろいろ試行錯誤している間に時間が過ぎてしまって、曖昧なまま終了しました。

私自身は、Type Assertionを使ったコードを書いて、確認し、2つ目の戻り値がtrueなら実装されている事がわかるので、その後、コードを書き直す。という理解をしたのですが、(コード作成時なのと、標準パッケージなので、実際にリリースするアプリケーションにそんな意味のないコードが必要とは思えないし…)

Type Assertionをしっかり理解していないとなかなか難しいところです。

さて、次回は、containerパッケージを見ていく予定です。

2014年1月9日木曜日

Golang Cafe #11を開催しました。

Golang Cafe #11を開催しました。

今回扱ったのはarchiveパッケージでarchive/tararchive/zipが含まれています。
サンプルコードはgithubにありますのでごらんください。

archiveパッケージの話題に入る前に、「プログラミング言語Goフレーズブック」での質問で、Type Assertionの例で書籍のサンプルを動かすとコンパイルエラーになる。interfaceの型に変えるとコンパイルが通る。という内容のものがありました。
該当のコードは以下のものになります。(以下のコードは修正済みです)
Example構造体がPublicインターフェイスを実装しているかどうかをチェックするために、e.(Public)という書き方をして、Type Assertionを使って確認するべきという趣旨の事が書かれていますが、なぜかコンパイルエラーということで悩みました。一見、正しいようにみえるのですが、Type Assertionの仕様を確認すると、インターフェイスの変数に対して行うもので、そもそも構造体変数に対して行うものではなかった。という結論に至りました。ぱっと見だとすぐに発見できないので、慣れておいた方がいいです。

package typeassertion

type Public interface {
        Name() string
}

type Example struct {
        name string
}

func (e Example) Nme() string {
//func (e Example) Name() string {
        return e.name
}

// func NewExample() Example {
//         return Example{"No Name"}
// }

func NewExample() Public {
        return Example{"No Name"}
}

func NewExample2() Public {
        var p Public
        e := Example{"No Name"}
        p.(Example)
//        e.(Public)

        return e
}

次に、本題のarchiveパッケージのサンプルを見ていきました。
まずは、tarのサンプルから。
ポイントは、tarに書き込むための、Writerを生成するために、tar.NewWriter()を呼び出します。引数はio.Writerなので、os.Create()などで、生成したFileを引数にすれば、ファイルに書き込む事ができます。
tar.NewWriter()で受け取ったWriterは必ずClose()してください。MacOSXだと、標準のアーカイブユーティリティで展開できない(エラーが発生する)という現象が出ました。
(ただ、Win版だと発生せず、OSに依らず、Goのプログラムから読み出すと読み出せたりするのですが…)

Writerを生成したら、あとは、Headerを書き、Bodyを書く。という事を繰り返せば、tarが完成します。

反対に、tarを読み出す時は、tar.NewReader()を呼び出し、Readerを生成します。
reader.Next()を呼び出すと、Headerが取れます。その後、readerからbodyを読み出せばファイルが取得できます。説明が雑なのでサンプルコードと一緒にごらんください。

次に、zipのサンプルですが、tarのサンプルを見た後なら、大体同じような構成なので、難しくないと思います。

zipを読み出す時もtarを読み出す時と変わらないので、特に難しくないでしょう。

結果、tarは圧縮するものではないので、容量が変わらないのは良いとして、zipは多少圧縮されるのかな?と思ったらそんなに変化が無く、画像を扱ったからかな?と思いましたが、テキストにすれば変化が出るのでしょうか…。

で、ディレクトリを走査して、大量のテキストをzipに入れようと試みましたが、時間がなく、stackoverflowのサンプルを発見した所で終わりました。ポイントは、path/filepath#Walk()を使って、再帰的に探していくという事になります。
これは、今後の課題になると思います。

次回は、compressパッケージをいじる会になります。

2014年1月4日土曜日

2014年の目標

転職したおかげで、今年の年末年始は長かったのですが、年末年始も色々と作業をしていたら、目標を設定する事ができていませんでした。
今年は仕事面はいろいろ制約があるので、GDG中国の活動、技術の2点から目標を定めようと思います。

GDG中国

毎年のことですが、まず、確定している分をこなす。

  • Golang Cafe(毎週日曜)
  • 第23回勉強会@岡山(2/11(火祝))

今年は、Golangが「盛り上がる」という段階から「使われる」段階になると思われるのでここで、しっかりアドバンテージを持っておきたいと思っています。実際、使っているという意味では、私はGAE/Gで利用していますし、Golang Cafeなどでもしっかり使えているのかなと思っています。そろそろ、何かのアプリケーションを実際に作る(GAE/Gではないプラットフォームで動作するようなものを作る)ということもしてみたいです。

まだ、あまり扱えていない内容の事をやる。使った経験が増えれば、導入も増える…はず?

  • Google AppsのAPIを扱うイベントをする。
    • Google Apps Scriptからではなく、いろんなアプリケーションから利用するもの。当然、Google Apps Scriptもやりますよ。
  • Google App Engine関係
    • 2013年はこの辺りの内容をあまり扱えなかったので。
  • Google Compute Engine
    • 課金しないと使えないけど、この辺の内容も扱いたい。

毎回、講師を私が担当することが多かったけど、以前のように、もう少しスタッフや、他の人に頼る事にしようと思っています。2月は今のところ、私が全て担当する予定ですが、「なんか違う」感が非常に強くなってきているし、今の職業の特性上、4月以降(今年も続けば)の負荷が上がるので恐らく無理が発生するんだろうなと思っています。

後は、会場の関係もあって、規模が大きいイベントが少なくなりそうです。Google I/O報告会とかがあっても、今年からはちょっとやれるかどうかわかりません。
広島国際学院大学 袋町キャンパスの閉鎖は非常に残念でした。
GDG Devfestなどで、「参加者全員ができるだけ近くに」と思って、広島を選択していたのですが、(と言っても、思惑と違って、四国会場が松山で開催されましたが…)その選択ができなくなるのは残念です。音響設備とネットワークが揃っている場所をまた探さないといけません。(今のところ、1か所あるけど、不安な点はある)

  • 広島で開催する時には「合同開催」を前提にする。

昨年もこれを実現しようとして、htmldayの時に合同でやってみたのですが、少し残念な結果になってしまいました。年末も画策したのですが、場所の確保ができず…。ということで、2014年もどうにかして合同開催に持ち込んでやろうと思っています。

今年は、活動4年を迎えるので、色々と考えないといけないかもしれませんね。

技術関係

昨年は、フロントエンド(AngularJS/Bootstrap/Pure)に凝っていたような感じがしています。今年は、Dart(?)/AngularJSと、Golang、Google Apps Scriptを押していくことになるかと思います。というよりも、私のスキルがその辺りに収束していっているようにも感じられるので、2013年に引き続き、「マイナー系」で攻めていこうと思います。

とは言え、それらを使うのに関連した技術は使えないといけないので、やっぱり手広くなるのかな。と思ったり思わなかったり。

テストにこだわってみる。

ここ数年、「テスト」が注目されている。というよりは、普通に使われるようになったと思います。私は納品物がないのと、本業ではないのでテストをあまり書いていませんでしたが、テストを書いた時の安心感というか、確認したぜ感は非常に大きいですし、重要な要素だと思うので、2014年はテストを書くことにこだわってみたいと思っています。(ちなみに、私はTDDとかはどうでもいい派です。)GAE/Gのユニットテストもサポートされましたし、テストを書かないわけにもいかないだろうと思いますので。

ただ、テストを書いてからのリリースとなると、どうしても開発スピードが落ちてしまうというのが、以前から非常に気になっていて、皆さんどうしているんでしょうね。

と、いうところで、2014年も皆さんよろしくお願いします。