Javascriptプログラムの短縮
ページ公開後も、1byte でもプログラムの長さが減るように改良を加えた。1年かけて 37byte ほど縮めたが、そろそろ限界だとおもう。
ここまで偏執的に短くする必要があるのか、というと…半分は自分の趣味だ(笑)
しかし、半分は仕事上の必然だった。
これはプログラマの経験則だが、プログラムは、長さに応じた確率によって「バグ」が入り込む余地がある。
ここでの「長さ」は、「面積」と言い換えてもよい。単純にバイト数の大小ではなく、行数も関係する、ということだ。
なぜなら、バグの混入は、エディットの際の「うっかり1文字挿入」などで発生するから。
これらの「うっかり編集」の位置がランダムに発生するとすれば、エディタ上での面積が大きいほど可能性が高まることになる。
コンパイル言語なら、コンパイラがエラーを出してくれる可能性が高い。しかし、今回は Javascript で、ユーザーが操作するまで動作しない、という形でプログラムされている。
万が一うっかり編集でバグが混入すると、混入したことにも気づかず放置される可能性があるのだ。
今回のメールアドレス表記の手法は、クライアントが「スパムボットを除けられる形でメールアドレスを表示したい」と言ったことから考案した。
この仕事では、僕がサイトのプログラム構築を請け負ったが、今後のコンテンツ拡充はクライアント側の役割となる。つまり、メールアドレス表記プログラムは、自分の手を離れて保守されることになる。
商売のチャンスとしてのメール連絡を逃がさない、というのが至上命題で作られたプログラムだ。ここでバグが発生して、「メール連絡お断り」状態にしてはならない。
この必然から、メールアドレス表記部分のプログラムを、少しでも短くする必要があった。
なんの工夫もしないメールアドレス表記の場合、たとえば
と書けば、62byte で済む。アドレスの長さによって異なるが、一般に 60~70byte 程度と思う。これがプログラムの「最小サイズ」だ。スパム除けの機能を入れる場合、この最小サイズに付加される「コスト」がどの程度か、が一つの目安となる。
アドレスや機能が多少違うので一概に比較できないが、同じアドレスを、参考例1では 451byte 14行、参考例2では 436byte 7行、 参考例3では、なんと 1859byte 18行で表現する。
コストとして、プログラム容量が7~30倍、行数が7~18倍になった。
これは「Javascript が動作しない環境」への考慮をしない状態での値なので、考慮が必要ならプログラム容量はさらに増加する。
(参考例1では、Javascript が動作しない環境へのタグ追加を推奨しているが、このタグだけで 63byte ある。)
このページで扱った手法は、nobody@example.co.jp 宛のアドレス表記が 125byte 1行で、Javascript が動作しない環境への配慮も込みだ。
コストが2倍程度なら、「保守のしやすさ」だけに関して言えば及第点だと思う。2倍を切りたかった、というのが本音だが、現状ではこれ以上縮む気がしない。(今後も何か気づいたら改良するけど)
この手のプログラムでは、とにかく文字を「隠す」ために、アスキーコード化するものが多い。
Javascript の String.fromCharCode メソッドでは、アスキーコードを複数個渡し、文字列を得ることができる。だから、アスキーコードから文字列を作るのに、ループをまわす必要すらない。
でも、この方法だと、文字列部分は3倍以上になる。1文字が数字2桁で表されるとして、文字の区切りに , (カンマ)を入れる必要があるので、1文字を3文字に変換する形になるのだ。
自分が「もっと短くできるだろ」と思ったのは、そのようなプログラムを最初に見たからだった。
ちょっと気が利いたプログラムでは、換字式暗号を使うことが多い。
文字列から1文字を取り出し、アスキーコードを得て、数値に演算を施してから、先に書いた String.fromCharCode で文字に戻すのだ。
自分が作ったプログラムも、最初はこの形式だった。コードで3つずらす、いわゆる「シーザー暗号」だった。
Javascript では、「1文字取り出してコード化」ではなく、「文字列の、特定位置の文字をコード化」で表現される。 charCodeAt というメソッドだ。
charCodeAt して、String.fromCharCode することの繰り返し。このメソッドの名前は、規定されたものなのでこれ以上短くできない。プログラム短縮の最大の壁だった。
ふと気づいて、換字式暗号ではなく、転置式暗号に変えてみた。
転置式暗号の解読に必要な処理は、文字を取り出して連結するだけ。取り出しは文字列を「文字配列」とみなす C言語由来の表記により、配列の演算子のみで出来る。連結も + 演算子だけでできる。上述の「名前の長いメソッド」をすべて排除できた。
ただし換字式と違って「文字を取り出す位置」が複雑になる。このため、前処理のプログラムが多少長くなった。
結果としては、32バイトの短縮。短縮する際には「ひらめき」が必要なので3回に分けて短縮したのだが(20,5,7 byte づつ減らした)、結果を見ればなかなかに「劇的な短縮」だと思う。自画自賛だけど。
今後も、なにか思いつけば少しづつ短くする予定だが、そろそろ限界だと考えている。
どちらも紀元前から存在する暗号で、簡単に解読できる。
今回の目的は「解読されない暗号を作ること」ではなく、「人には簡単に解読されるが、ロボットを欺くには十分なものを作る」ことなので、これで十分。
おまけ
実際の稼動ページは、Javascript プログラムから、コメントやスペースや改行を抜いて短くしてある。
これでは、参考にするにも読みにくいと思うので、下に元のプログラムを公開する。(jQuery使用)
…もっとも、最初から短くする気で書いたので、読みやすいわけではない。