記号プログラミングで学ぶRuby1.9の仕様変更??

takesako
2010-12-13

またまた再登場のid:TAKESAKOです。
今日はRuby1.8で学ぶ、簡単?!記号プログラミングから発展した話題「記号プログラミングで学ぶRuby1.9の仕様変更??」についてお話ししたいと思います。

Ruby 1.8 と 1.9 の非互換

Ruby1.8以前では ?a は'a'のASCIIコードの数値97が返されていましたが、
Ruby1.9からは長さ1の文字列"a"を返すように仕様変更されました。

これはRuby1.9からの多言語対応のためで、文字列をバイト列ではなく文字単位で扱うようになったためです。

ruby1.9> p ?a
"a"

ruby1.8> p ?a
97

したがって、?? は Ruby1.9では文字列"?"のことですが、Ruby1.8以前では'?'のアスキーコードである63の数値を返すことになります。

ruby1.9> p ??
"?"

ruby1.8> p ??
63

文字列と数値の%演算子の違い

次に数値と文字列における%演算子の違いですが、文字列の"?"%"?"はsprintf("?","?")と同じ動きをして結果は"?"となりますが、数値の63%63は63÷63=商1…余り0の余りを返すため0となります。

ruby1.9> p ??%??
"?"

ruby1.8> p ??%??
0

これを利用してRuby1.9の処理系かどうかを記号9バイトだけで判定することができます。

記号のみでバージョン1.9判定

RUBY_VERSION の12文字を使うよりも圧倒的に短いです。地球に優しくてエコですね。:-)

ruby1.9> p ??%??==??
true

ruby1.8> p ??%??==??
false

記号のみで任意の文字列を作成する方法

Rubyで任意の文字列を生成するには""<<数値(ASCIIコード)という文字列の破壊的演算を行なう構文を利用するのが簡単です。

ruby1.9> p ''<<123
"{"

ruby1.8> p ''<<123
"{"

任意の数値を作成するには、Ruby1.8以前では?{-?|などを利用してできたのですが、Ruby1.9からは先の仕様変更があるため使えません。どうするかというと、正規表現を利用します。

ruby1.9> p //=~''
0

ruby1.8> p //=~''
0

正規表現にマッチした場所を文字列の先頭から数えて結果を返します。

ruby1.9> p /_/=~'>_'
1

ruby1.8> p /_/=~'>_'
1

これを利用すると、こちらで用意した文字列から目的の先頭バイト数でマッチする正規表現を書いてあげれば、任意の数値を作成することができます。これは記号だけを使って実現できます。

ruby1.9> p /_/=~'>>>>>>>>>_'
9

ruby1.8> p /_/=~'>>>>>>>>>_'
9

(''<<数値)で任意のASCIIコードの文字を標準出力 $> に書き込む

以上のテクニックを組み合わせると、$> << "文字列" と書くことを利用して任意の文字列を標準出力に書き込むことができます。

ruby1.9> $><<(''<<88)+(''<<89)+(''<<90)
XYZ

ruby1.8> $><<(''<<88)+(''<<89)+(''<<90)
XYZ

これで、Ruby1.8とRuby1.9のどちらのバージョンでも動くプログラムを作成できました。

$><<(''<<(88)<<(89)<<(90))

これを「Hello, world!\n」を出力するようにASCIIコードの数値を調整して

$><<(''<<(72)<<(101)<<(108)<<(108)<<(111)<<(44)<<(32)<<(119)<<(111)<<(114)<<(108)<<(100)<<(33)<<(10))

それぞれの数字を返す正規表現に置き換えると

$><<(''<<(/=/=~'(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^=')<<(/=/=~'(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_=')<<(/=/=~'(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_=')<<(/=/=~'(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_=')<<(/=/=~'(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/=')<<(/=/=~'(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^=')<<(/=/=~'(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^=')<<(/=/=~'(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;=')<<(/=/=~'(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/=')<<(/=/=~'(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^=')<<(/=/=~'(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_=')<<(/=/=~'(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^=')<<(/=/=~'(^_^)/;(^_^)/;(^_^)/;(^_^)/;(^_^)=')<<(/=/=~'(^_^)/;(^_='))

無事、顔文字だらけになりました。めでたし、めでたし。