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のみで動かすことができます。

これにを使えば、RubyJavaScriptそれぞれのコードを含めることができますが、それでは面白くないので、fizzbuzzのロジック部分は共通化しました。

FizzBuzz部分

まず、varを使って変数宣言をします。怖い人に睨まれないように、JavaScriptで'use strict'を使っているので、varでの変数宣言が必要です。一方、Rubyにはvarキーワードがないので、define_singleton_methodでvarメソッドを定義しています。(2行目)

次に、1から100までループします。ここはうまく共通化できなかったので、先ほどの要領でJavaScriptRubyで書き分けています。ループの変数は共通して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