pockestrap

Programmer's memo

Ruby Puzzle その1

解答、ヒント、解説などは充分な余白を開けた上で表示するので、安心して読んでください。

問題

gist.github.com

このRubyプログラムに1文字追加して、"Hello world\n"を出力するプログラムにしなさい。 なお、例外などで異常終了するものは正答とはみなしません。

RubyKaigi 2019のCookpadのパズルリスペクトです。

techlife.cookpad.com

なお、hanachinさんが色々な人が出している問題をまとめてくれているので、もっと問題を解きたい方はそちらをご覧ください。

Okinawa.rb Ruby Puzzles 2019/5/15 - #rubykaigi のRubyPuzzleが面白かったので真似しました。Ruby 2.7でしか動きません。 · GitHub

ヒント

以下にヒントを少しずつ書いていきます。適当にスクロールしてください。 ヒント3まであり、少しずつ核心に近づいていきます。











































ヒント その1

Lexerの気持ちになりましょう。











































ヒント その2

Rubyparser_yylex関数を読んでみましょう。parse.yにあります。











































ヒント その3

ほぼ答えです。

docs.ruby-lang.org











































答え

想定解は、6パターンあります(多分)。3種類の文字を、プログラム中のある2箇所に入れることで"Hello world\n"を出力するプログラムとなります。

ここではその答えを1つ示します。

ですが、答えのプログラムを直接書くのはすこし難しいので、答えのプログラムを出力するRubyのプログラムを書きます。

# ans.rb
puts <<~"RUBY"
  puts("Hello world")\x04 if false \
    && false &&
    false
RUBY

このプログラムの出力をRubyプログラムとして実行すると、"Hello world\n"が出力されます。

$ ruby ans.rb | ruby
Hello world

解説

ヒント その3にあるとおり、Rubyのプログラムの終わりにはいくつかの方法があります。

docs.ruby-lang.org

ファイルの終り(文字列をevalしている場合は文字列の終り)

これは普通の方法ですね。

ENDのみの行(前後に空白があると認識されません)

__END__とだけ書かれた行があると、その行以降はRubyのプログラムとしては扱われず、DATAという定数から参照できます。

^D(コントロールD) 、Z(コントロールZ)

これが今回の答えです。

ソースコード中に直接このコントロール文字を埋め込むと、そこでRubyプログラムが終了します。 今回はif false && false && falseの部分がなかったことになればHello worldが出力されるので、ifの直前にこのコントロール文字を入れてやることで、ifを無視することができます。

便利ですね。