A Tour of Goの練習問題を解説するシリーズ(8/11) – Exercise: rot13Reader

みなさん、こんにちは。人類をGopherにしたいと考えているまるりんです。

A Tour of Goはプログラミング言語Goの入門サイトです。 このシリーズではA Tour of Goの練習問題を解説します。

今回は以下の問題を扱います。

問題
Exercise: rot13Reader
解答
https://play.golang.org/p/tHrlg1fW6Fh


問題に以下のようにあります。

例えば、 gzip.NewReader は、 io.Reader (gzipされたデータストリーム)を引数で受け取り、 *gzip.Reader を返します。 その *gzip.Reader は、 io.Reader (展開したデータストリーム)を実装しています。

gzip.Read()を参考にすると、レシーバからRead()を呼び出し、その引数にラップされたRead()の引数を渡しています。まずこの動きを真似ます。

ソース
https://play.golang.org/p/OOTFtVFQ1sR

実行結果

Lbh penpxrq gur pbqr!

io.Copy()を通じてmain()で設定している文字列が表示されました。この文字列に対しROT13換字式暗号を適用していきます。 Wikipediaによると、ROT13換字式暗号は単換字式暗号(シーザー暗号)の一種であり、アルファベットを一文字毎に13文字後のアルファベットに置き換える(e.g. AはNに、BはOに置き換える)ようです。

ネットの解答では難しそうな計算をしているのですが、まるりんの頭で扱うには複雑なため簡単な方法がないか考えました。

コードに複雑さを感じたのでデータ構造に複雑さを寄せることにしてコードを単純化します。文字への加算によってZからはみ出した場合の処理が複雑さを生んでいると思います。 そのため、はみ出ても問題ない構造にします。'z' + 1が'a'であるような構造です。 アルファベット全体が2つ連続している文字列を作ります。ローテーションの演算ではその文字列のインデックスを計算します。 具体的には以下のロジックです。

const Alphabet = "abcdefghijklmnopqrstuvwxyz"
const Table = Alphabet + Alphabet
idx := c - 'a' + 13
rotatedCh := Table[idx]

大文字と小文字の場合に処理を分けるのも煩雑ですので、すべて小文字に変換し、ローテーション後に小文字にしたものを大文字に戻しています。 また、アルファベットを間違えずに正確に手入力することはたいへんなので自動生成します。

$ jot -w %c 26 a | perl -pe 's/\n//'
abcdefghijklmnopqrstuvwxyz%

これらを踏まえて問題を解きます。

ソース
https://play.golang.org/p/tHrlg1fW6Fh

実行結果

You cracked the code!

この解答が論理的に一番シンプルだと思います。