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

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

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

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

問題
Exercise: rot13Reader
解答
https://go.dev/play/p/f9SxcnBLdZx


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

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

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

ソース
https://go.dev/play/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 := ch - 'a' + 13
rotatedCh := table[idx]

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

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

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

ソース
https://go.dev/play/p/f9SxcnBLdZx

実行結果

You cracked the code!

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