JavaScriptとして動作するfizzbuzz.rb
LL Decadeで、Polyglotの話を聞いて面白かったので、私もやってみました。
そこで、FizzBuzzを書いてみました。
fizzbuzz.rbですが、JavaScriptとしても動作します。Rubyのバージョンは1.9です。
'use strict'; "#{define_singleton_method(:var){|*v|}||";/*"}" # */ var puts=console.log; /* # */ Number.prototype.to_s = function(){return this;}; var max=100, a=0, b=0; "#{";/*"}" (1..max).each { |i| # */ for(var i=1;i<=max;i++){ a = i % 3 < 1; b = i % 5 < 1; puts((a ? 'fizz' : '') + (b ? 'buzz' : '') + (!a && !b ? i.to_s() : '')); }
JavaScriptでのシンタックスハイライト
'use strict'; "#{define_singleton_method(:var){|*v|}||";/*"}" # */ var puts=console.log; /* # */ Number.prototype.to_s = function(){return this;}; var max=100, a=0, b=0; "#{";/*"}" (1..max).each { |i| # */ for(var i=1;i<=max;i++){ a = i % 3 < 1; b = i % 5 < 1; puts((a ? 'fizz' : '') + (b ? 'buzz' : '') + (!a && !b ? i.to_s() : '')); }
解説
ポイントは、2行目の"#{define_singleton_method(:var){|*v|}||";/*"}"です。
Rubyでは#{}が式展開されますが、JavaScriptでは展開されません。これを利用して、JavaScriptでのみコメントを開始しています。
この状態で、rubyのコードを書くと、JavaScriptではコメントアウトされているので、Rubyのみで実行することができます。
さらに、 #*/ (JavaScriptのコード) /* とやると、Rubyではコメントアウトされたまま、JavaScriptのみでコメントを解除できるので、JavaScriptのみで動かすことができます。
これにを使えば、Ruby、JavaScriptそれぞれのコードを含めることができますが、それでは面白くないので、fizzbuzzのロジック部分は共通化しました。
FizzBuzz部分
まず、varを使って変数宣言をします。怖い人に睨まれないように、JavaScriptで'use strict'を使っているので、varでの変数宣言が必要です。一方、Rubyにはvarキーワードがないので、define_singleton_methodでvarメソッドを定義しています。(2行目)
次に、1から100までループします。ここはうまく共通化できなかったので、先ほどの要領でJavaScriptとRubyで書き分けています。ループの変数は共通してiです。
fizzbuzzでは一般的には分岐が必要ですが、if文がうまく使えなかったので、三項演算子を使いました。
Rubyでは、文字列と数値は+では結合できません。なので、to_sで文字列に変換することにしました。そのため、JavaScriptでも動くようにNumberのprototypeにto_sを追加しています(4行目)。
最後に、各行をputsで出力しています。JavaScriptでputsを使うために、putsにconsole.logを定義しています。そのため、実行のためにはconsole.logが定義されている必要があります。
元ネタ
記号でPolyglotプログラミング♪: http://developer.cybozu.co.jp/.s/takesako/2009/07/polyglotrejectk.html