※面白いものが出来上がったので、Advent Calendarに書いていた当初の予定を変更してお送りします。
まずは見た目のインパクトから
require './masshiro_decode'
masshiro_decode
を用意した上で、このプログラムをRubyとして実行すると、「Hello, world of white Ruby!
」という文字列を表示します。
作ったきっかけ
だいぶ前に書かれたこちらの記事にあるように、RubyではASCII外の文字をなんでも識別子に使えます。そして、UnicodeにはASCII外のスペースが10種類以上あるので、「これを使って識別子を組み立てれば、真っ白いRubyプログラムも作れるんじゃないか?」とは思っていたのですが、ずっと放置していました。
カレンダーのねたになりそうなので、発掘して投下した次第です。
動作の仕組み
種明かしに、デコード部分を書いていきます。
SPACE_CHARS = "\u00a0\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u3000".freeze
REPLACE_CHARS = '0123456789a'.freeze
def method_missing(name, *args)
name_s = name.to_s
super unless name_s.start_with?("\u00a0")
val = name_s[1..-1]
print val.tr(SPACE_CHARS, REPLACE_CHARS).to_i(SPACE_CHARS.length).chr
end
まずはASCII以外のスペースがあると、不明な識別子なのでmethod_missing
が発動します。そして、
(U+00a0)で始まらないものであればデフォルト実装に流してしまいます1。このスペースで始まる場合は、残りの11種類2のスペースで文字がエンコードされているので、「ふつうの11進法に戻す→整数にする→chr
で文字化」という流れで文字に戻して、文字が出力されます。
エンコード部
もちろん、こんなコードを手で書くのは視認性も入力性も最悪なので、自動生成で作りました。
module Masshiro
SPACE_CHARS = "\u00a0\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u3000".freeze
REPLACE_CHARS = '0123456789a'.freeze
module_function
def encode_num(num)
num.to_s(SPACE_CHARS.length).tr(REPLACE_CHARS, SPACE_CHARS)
end
def decode_num(str)
str.tr(SPACE_CHARS, REPLACE_CHARS).to_i(SPACE_CHARS.length)
end
def encode_print(str)
ret = "require './masshiro_decode'\n"
str.each_char do |c|
ord = c.ord
ret << "\u00a0" << encode_num(ord) << "\n"
end
ret
end
end
さらに妄想
eval
を組み込めば、どんなプログラムも驚きの白さになると思いますが…実装予定は未定です。
おことわり
環境によっては、全部スペースに見えないことがありますので、その点はご了承ください。また、Ruby 2.2.4で実行しています。