世界の Rubyist が集う RubyKaigi というイベントに時々併設される、 TRICK というコンテストがあります。 「一番変なRubyプログラムを書いたヤツが勝ち」というコンテストです。 前々回(Rubyでいろは歌)、 そして 前回(Rubyで円周率覚え歌) と、なんとも驚いたことに私が2回続けて優勝していたのですが、今年が第3回。 やりました。3連覇を達成しました!今回のネタは…
Ruby の言語仕様のページに、 予約語の一覧を表にしたものがあるんですよ。これです。
これの真似をして予約語を並べてみました、というのが今回の作品です。
alias BEGIN for unless def class
super true or return defined? next
break while begin undef do end
rescue then retry else undef module
nil ensure case if yield __LINE__
self and redo elsif not __FILE__
alias END in end when __ENCODING__
end until false end
ただし、元の表とは違って、Rubyのプログラムとして読めるものになっていて、 構文解析エラーも実行時エラーも警告も出ずに実行できます。実行しても特に何もしないんですが、 何もしないプログラムが優勝するコンテストってすごいですね。
見所としては
def
や alias
や defined?
や
undef
という、引数に予約語すら取ることができる構文に投げ込んで捨てます。
↑のコードを書くために、Rubyの構文解析の実装である parse.y を穴が開くほど眺めて、 どこなら改行入れられるのか省けるのか、予約語捨てポイント他にないか、等々探していたのですが、 その副産物でもう一個できたので投稿してみたのがこちら。13位入賞。
def STDOUT.write (s); syswrite s
end if def $>.write; end
s = Array.new(){}.map{|s|}
s << (-"Trick").grapheme_clusters{} [0] # frozen_string_literal: Trick
s << ("Ruby".unpack *"ao") [0]
s << "#{10**2018 + 1e2018}" [0]
s << "#{1>2>3 rescue $!.class.trust.class}"[0]
s << "#{true; Kernel.public_class_method}" [0]
s << ($ruby.object_id.coerce +2018) [0]
result = puts s*""
普通に実行すると普通に TRICK2018 と表示するだけなんですが、
$ ruby entry.rb TRICK2018
警告を有効にすると、全部の行が2回ずつと、すごい勢いで警告されるプログラムになっています。
$ ruby -w entry.rb entry.rb:1: warning: parentheses after method name is interpreted as an argument list, not a decomposed argument entry.rb:2: warning: mismatched indentations at 'end' with 'def' at 1 entry.rb:3: warning: shadowing outer local variable - s entry.rb:4: warning: `frozen_string_literal' is ignored after any tokens entry.rb:5: warning: `*' interpreted as argument prefix entry.rb:6: warning: Float 1e2018 out of range entry.rb:7: warning: comparison '>' after comparison entry.rb:8: warning: unused literal ignored entry.rb:9: warning: ambiguous first argument; put parentheses or a space even after `+' operator entry.rb:10: warning: assigned but unused variable - result entry.rb:1: warning: method redefined; discarding old write entry.rb:2: warning: previous definition of write was here entry.rb:3: warning: given block not used entry.rb:4: warning: passing a block to String#grapheme_clusters is deprecated entry.rb:5: warning: unknown unpack directive 'o' in 'ao' entry.rb:6: warning: Bignum out of Float range entry.rb:7: warning: trust is deprecated and its behavior is same as untaint entry.rb:8: warning: public_class_method with no argument is just ignored entry.rb:9: warning: global variable `$ruby' not initialized entry.rb:10: warning: #<IO:<STDOUT>>.write is outdated interface which accepts just one argument TRICK2018
最初の10個の警告は静的な、構文解析時の警告で、 次の10個の警告は動的な、実際にその行を実行したときに出る警告です。
実行時警告の1番と2番の処理が自分では気に入っています。 これは実は実際は2行で1つの警告で、メソッド再定義の警告が前の定義箇所も同時に警告しているのですが、 if の修飾子を使って、1→2 の順に出るように操っています。 実行時の10番目も結構好きで、警告されながら文字を出力する方法ってあるかなーと思ってみると、あるもんなんですね。
parse.y をずっと眺めていたら、「この rb_warn とか rb_warning を全部踏むプログラムって面白いかな」 とふと思ったのがこっちを書き始めたきっかけなのですが、実は出し方がわからない警告があったので諦めました。
や
なんですが、どなたか出し方教えてください。後者は出せるは出せるんですが、 出すと必ず同時に構文エラーになってしまって、動かせる Ruby プログラムにならない…。
といったところです。正直、去年と違って出来映えに自信は全くなかったですし、 発表になって2位~12位の入賞作品を見ても技巧が冴え渡りすぎていて、 あんな素晴らしい作品群をさしおいてこんなのが評価していただいていいのか、 という気持ちが結構あります。まあ巧く審査員のツボをつけたのでしょう。 ありがたいことです。
TRICKの公式サイト に他の方の入賞作品もすぐに掲載されると思いますので、ぜひぜひ読んでみてください。 ほんとすごいです。