1. Qiita
  2. 投稿
  3. Ruby

真っ白いRubyプログラム

  • 29
    いいね
  • 1
    コメント
に投稿

※面白いものが出来上がったので、Advent Calendarに書いていた当初の予定を変更してお送りします。

まずは見た目のインパクトから

本当にプログラム?
require './masshiro_decode'
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
    
   

masshiro_decodeを用意した上で、このプログラムをRubyとして実行すると、「Hello, world of white Ruby!」という文字列を表示します。

作ったきっかけ

だいぶ前に書かれたこちらの記事にあるように、RubyではASCII外の文字をなんでも識別子に使えます。そして、UnicodeにはASCII外のスペースが10種類以上あるので、「これを使って識別子を組み立てれば、真っ白いRubyプログラムも作れるんじゃないか?」とは思っていたのですが、ずっと放置していました。

カレンダーのねたになりそうなので、発掘して投下した次第です。

動作の仕組み

種明かしに、デコード部分を書いていきます。

masshiro_decode.rb
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で文字化」という流れで文字に戻して、文字が出力されます。

エンコード部

もちろん、こんなコードを手で書くのは視認性も入力性も最悪なので、自動生成で作りました。

masshiro.rb
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で実行しています。


  1. これ以外の文字で始まる命令は、将来のために予約されています(違) 

  2. Unicodeのスペースはもう少し種類があるのですが、幅がゼロのものはコピペ漏れなどトラブルになりかねないので除外しています。