A Tour of Go の中に ROT13 を実装するエクササイズがあったので、今回は試しにそれを書いてみる。
ROT13 はシーザー暗号の一種で、文章の中の各文字をそれぞれ 13 文字分ずらした内容に変換するというもの。 アルファベットが 26 文字なので、単純に二回 ROT13 を適用すれば元の文章に戻すことができる。
書いた内容は次の通り。 説明についてはコメントを適宜入れてある。 ポイントは rot13Reader 構造体が Read(p []byte) (n int, err error) メソッドを実装することで io.Reader インターフェースとして動作するようにしているところかな。 Golang ではインターフェースを明示的に implements するのではなく、同じ名前とシグネチャと返り値のメソッドがあればそのオブジェクトとして振る舞うことができるという、ダックタイピングに近い考え方に則って継承と同等の機能を実現している。
package main import ( "io" "os" "strings" ) func rot13(c byte) byte { // 文字を ROT13 変換する関数 switch { case ('A' <= c && c <= 'Z'): // 13 文字分ずらす return (c - 'A' + 13) % 26 + 'A' case ('a' <= c && c <= 'z'): // 13 文字分ずらす return (c - 'a' + 13) % 26 + 'a' default: // 何もしない return c } } type rot13Reader struct { // io.Reader をラップする構造体 r io.Reader } func (r *rot13Reader) Read(p []byte) (n int, err error) { // バイト列を読み込む n, err = r.r.Read(p) if err != nil { // 読み込みに失敗した return 0, err } for i := range p { // 各文字に ROT13 を適用する p[i] = rot13(p[i]) } return } func main() { // 文字列から io.Reader インターフェースを実装したオブジェクトを生成する s := strings.NewReader("Uryyb, Jbeyq!") // ROT13 変換するオブジェクトでラップする r := rot13Reader{s} // 標準出力に書き出す io.Copy(os.Stdout, &r) }
実行すると変換後の文字列が表示される。
$ go run rot13.go Hello, World!
ばっちり。