トップ «前の日記(2007-11-30) 最新 次の日記(2007-12-02)» 編集

Route 477



2007-12-01

[javascript] JSONをevalするときは括弧で囲まないといけないらしい?

eval("{}")

これはundefinedだが

eval("({})")

これは Object のインスタンスを返す。

JSONをevalするときは

result = eval("("+json+")");

とするのがセオリーなのかな?(prototype.jsはそうなっている)

[ruby][scheme] S式をRubyで手軽に生成する

ちょっと気の利いたWebAPIを作ったとき、フォーマットはJSONかXMLが代表的なんだけど、 ことSchemeから使うのならS式の方が使いやすいのは容易に想像できると思われます。 が、JSONは gem install json すれば JSON.generate(obj) で手軽に生成できるのに対し、 S式はRubyから手軽に生成する方法がありません。困りましたね。

というわけで、S式をRubyで手軽に生成するコードを書いてみましたよ。

sexp.rb:

class Array
  def to_sexp
    items = self.map{|item|
      case item
      when true
        "#t"
      when false
        "#f"
      when Array
        item.to_sexp
      when Symbol
        item == :dot ? "." : "'#{item}"
      when Float
        if (i = item.infinite?)
          i == 1 ? "+inf.0" : "-inf.0"
        else
          item.to_s
        end
      else
        item.inspect
      end
    }.join(" ")
    ["(", items  , ")"].join
  end
end

こんな感じで使えます。

[1, [2, 3], 4].to_sexp   #=> "(1 (2 3) 4)"

ドット対の表現がちょっと格好悪いんですけど、手軽さのためと割り切って我慢してください。

[1, :dot,  4].to_sexp    #=> '(1 . 4)'

余談

ちなみにS式はリストなので、Rubyの配列とそのまま対応するものではありませんよ。

'(1 2 3) というリストは、無理やり配列で表現すると[1, [2, [3, nil]]] のようになります。 '(1 (2 3) 4)というリストは、[1, [ [2, [3, nil]], [4, nil]] のようになります。 実際にセルの絵を書いてみるとよく分かると思います。

これを手書きするのはかなり辛いので、上のコードではそのへんを割り切った仕様にしてみました (だいたいSchemeの「()」を「[]」に直せばOKなように)。

テスト

RSpecによるテストです。

require 'sexp'

describe "sexp" do
  context "nil" do
    [].to_sexp.should == "()"
  end

  context "single" do
    [1].to_sexp.should == "(1)"
  end

  context "double" do
    [1, 2].to_sexp.should == "(1 2)"
  end

  context "triple" do
    [1, 2, 3].to_sexp.should == "(1 2 3)"
  end

  context "string" do
    [1, "asdf", 3].to_sexp.should == '(1 "asdf" 3)'
  end

  context "nest" do
    [1, [2, 3], 4].to_sexp.should == '(1 (2 3) 4)'
  end

  context "dot pair" do
    [1, :dot,  4].to_sexp.should == '(1 . 4)'
  end

  context "symbol" do
    [:a, :b, :c].to_sexp.should == "('a 'b 'c)"
  end

  context "boolean" do
    [true, false].to_sexp.should == "(#t #f)"
  end

  context "infinity" do
    [1.0/0, -1.0/0].to_sexp.should == "(+inf.0 -inf.0)"
  end
end