Derive Your Dreams

about me
Twitter: @kinaba

21:24 18/06/02

TRICK 2018 (FINAL) 3連覇!

世界の 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のプログラムとして読めるものになっていて、 構文解析エラーも実行時エラーも警告も出ずに実行できます。実行しても特に何もしないんですが、 何もしないプログラムが優勝するコンテストってすごいですね。

見所としては

警告祭り (13位)

↑のコードを書くために、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の公式サイト に他の方の入賞作品もすぐに掲載されると思いますので、ぜひぜひ読んでみてください。 ほんとすごいです。

presented by k.inaba (kiki .a.t. kmonos.net) under CC0