yhara.jp

Recent Posts

Ruby本体の「僕の作品」を修正した話

2018-03-13
Tech

昨年秋のRubyKaigiに参加したとき、コミッタである中田さんから私の書いたコードがRuby trunkで壊れているという連絡をいただいた。そこで、それを修正するプルリクエストを書いた。

といっても、diffは以下の1行だけ。

-_ {                 return                  }
+_ {                  yield                  }

一体何を直したか分かるだろうか?

TRICK 2013

ヒントはファイル名にある。

trick2013というのは、過去に行われた「普通でないRubyプログラムのコンテスト」だ。IOCCCのRuby版、といえば伝わる人には伝わるだろう。

私は2つのプログラムを応募して、そのうちの1つが4位に入賞した。誰が言い出したのか、コンテストの特典として入賞した作品はRubyのsampleディレクトリにコミットされるというものがあり、かくして私の「普通でないRubyプログラム」がRubyのソースコードに同梱されることになった。

entry.rb

入賞した作品というのがこれだ。

def _(&b)$><<->(x){x ? (String===x ?x.upcase:
(Class===x ? x : x.class).name[$a?0:($a=5)]):
" "}[ begin b[];rescue Exception;$!;end ] end

_ {                 return                  }
_ {            method(:p).unbind            }
_ {                eval "{ "                }
_ {           Thread.current.join           }
_ {                   nil                   }
_ {                 select                  }
_ {                  ruby                   }
_ {               self.class                }
_ {          Thread.current.group           }
_ {                nil.to_h                 }
_ {          "\xFF".encode("big5")          }
_ {                  raise                  }
_ {                 [0][1]                  }
_ {           Regexp.compile "*"            }
_ {           RUBY_COPYRIGHT[32]            }
_ {                 binding                 }
_ {            :s.class.name[1]             }
_ {                  warn                   }
_ {                [a: :b][0]               }
_ {                 methods                 }
_ {                IO.class                 }
_ {               {}.fetch(0)               }
_ {                open " "                 }
_ {               1000000.chr               }

実行すると以下のように出力される。

$ ruby entry.rb
JUST ANOTHER RUBY HACKER

「JUST ANOTHER RUBY HACKER」というのはこの手のプログラムによくある出力で、もとはPerlの文化だと思われる。

仕組み

動作を簡単に解説しておく。まず、最初の3行で_というメソッドを定義し、後半ではそれを使って1行ごとに1文字を出力している。

文字の生成方法は、3行目のrescue Exceptionが肝で、各ブロック内で例外を起こし、その名前から文字を拾うというのが基本戦略だ。以下を見れば、頭文字が文章になっていることが分かるだろう。

_ {                 return                  } # Local"J"umpError
_ {            method(:p).unbind            } # "U"nboundMethod
_ {                eval "{ "                } # "S"yntaxError
_ {           Thread.current.join           } # "T"hreadError
_ {                   nil                   } #
_ {                 select                  } # "A"rgumentError
_ {                  ruby                   } # "N"ameError
_ {               self.class                } # "O"bject
_ {          Thread.current.group           } # "T"hreadGroup
_ {                nil.to_h                 } # "H"ash
_ {          "\xFF".encode("big5")          } # "E"ncoding::InvalidByteSequenceError
_ {                  raise                  } # "R"untimeError
_ {                 [0][1]                  } #
_ {           Regexp.compile "*"            } # "R"egexpError
_ {           RUBY_COPYRIGHT[32]            } # "U"
_ {                 binding                 } # "B"inding
_ {            :s.class.name[1]             } # "Y"
_ {                  warn                   } #
_ {                [a: :b][0]               } # "H"ash
_ {                 methods                 } # "A"rray
_ {                IO.class                 } # "C"lass
_ {               {}.fetch(0)               } # "K"eyError
_ {                open " "                 } # "E"rrno::ENOENT
_ {               1000000.chr               } # "R"angeError

Ruby 2.5

ところが昨年秋にRuby trunkに入った修正で、このプログラムが動かなくなった。1行目のブロック内でのreturnが許容されるようになったので、LocalJumpErrorが起こらなくなってしまったのだ。

まあこんなプログラムが動かなくなっても問題はないのだけど、しかしsample以下にコミットされているプログラムが"正しく"実行できないというのは宜しくない気もする。

そういうわけで、作品性を損なわない形でRuby 2.5用の修正を施したのが、冒頭の

-_ {                 return                  }
+_ {                  yield                  }

というプルリクエストの正体だったのだ。

TRICK 2018

さて、どうして今ごろになってこんな記事を書いているかというと、このコンテストが再び行われているからだ。

締切は今月末の3/31。発表は5月のRubyKaigi 2018 仙台で行われる。"FINAL"と付いている通り、mameさん主催での開催は次回の予定はないとのことなので、まだ応募していないアイデアがある人は応募してほしいし、そうでない人も応募してほしい。過去の作品については以下が参考になるだろう。

More posts

Posts

(more...)

Articles

(more...)

Category

Ads

About

About the author