SyntaxHighlighter

StackEdit CSS

2013年12月13日金曜日

ratex(gem)の解説

これは、Ruby Advent Calendar 2013の11日目の記事です。完全に忘れてました…なんらかの通知メールが来てくれればいいのだが…

この日の前の方々の記事からして、自分の記事は自作のgemの解説とかでいいのかよ…と思いつつ書きます。(浮くの覚悟です)

ratexは、Rubyの式をTeXの式に変換するgemです。使い方は Rubyの式をTeXの数式に変換するやつ作ったを見てください。ソースコードはhttps://github.com/long-long-float/ratexにあります。

この解説は勉強の一環で書いたもので、間違っているかもしれません。そういうときは優しくツッコんでもらうと喜びます。

1 + 1 ≠ 2

1 + 1

の挙動から説明します。ratexは変換する前に、FixnumStringSymbol+などの演算子メソッド(?)を上書きします。具体的には、aliasしてから定義します。

OPERATORS = [:+, :-, :*, :/, :**, :==, :+@, :-@, :<, :>]
KLASSES = [Fixnum, String, Symbol]

KLASSES.each do |klass|
    klass.class_eval do
        OPERATORS.each do |ope|
            if method_defined? ope
                alias_method "#{ope}_", ope
            end
        end

        #色々な演算子を定義する…
    end
end

ここで例えば、+を下のように定義すると、1 + 1が文字列として返ってきます。

def +(other)
    "#{self} + #{other}"
end

さて、上のaliasgenerateが終わったら戻さないといけません。さっきの逆をします。

KLASSES.each do |klass|
    klass.class_eval do
        OPERATORS.each do |ope|
            remove_method ope
            if method_defined? "#{ope}_"
                alias_method ope, "#{ope}_"
            end
        end
    end
end

これにより、

1 * 2 + 3 * 4 + 5

というようなやや複雑(?)な式も普通に変換出来ます。どういうことかというと*+より結合度が高いので、

"1 * 2" + "3 * 4" + 5
"1 * 2 + 3 * 4" + 5
"1 * 2 + 3 * 4 + 5"

となります。Rubyのパーサーをいい感じに利用することで、結構手抜きできました。試していないのでわかりませんが多分、構文木も起こせるかもしれません。

Contextの導入

上だけでは簡単な計算、しかもFixnum,String,Symbolしか使えません。xなどの変数も使いたくなるでしょう。そこで、Contextというクラスを導入しました。

class Context
    def method_missing(name, *args)
        name.to_s
    end
end

と定義して、Contextのインスタンスに対して変換したい式をinstance_evalに渡せばx + 1のような式も動くんじゃねという魂胆です。Contextという名前は他に名前が思いつかなくて、なんとなくこれな気がしたので付けただけです。このテクニック(?)は、内部DSLで使われているようです(というかこれで知った気がする)。

さて、これで

i + 1

が動くようになりました。

sinなどの関数も扱いたくなりました。そういう時はContext

def sin(expr)
    "\\sin(#{expr})"
end

と書けばちゃんと動いてくれます。調子に乗って、sqrtも作ってみました。

def sqrt(expr, n = 2)
    "\\sqrt" + ((n != 2)? "[#{n}]" : "") + "{#{expr.to_s}}"
end
Ratex.generate{ sqrt(x) } # => "$$\\sqrt +  + {x}$$"

おかしいです。sqrtの式がそのまま変換されてしまったようです。そうです。aliasしていたのを忘れてました。aliasするのをbegin_generate, 戻すのをfinish_generateとすると、

def out_of_generate
    finish_generate
    ret = yield
    begin_generate
    ret
end

を定義して

def sqrt(expr, n = 2)
    @gen.out_of_generate do
        "\\sqrt" + ((n != 2)? "[#{n}]" : "") + "{#{expr.to_s}}"
    end
end

とすればちゃんと動くようになります。ブロック便利ですね。

out_of_generateを至るところに書いていくのですが、遅くならないのか?と思われるかもしれません。パフォーマンス度外視です。

not = but ==

===となってしまいました。=は、

def ほげほげ=(other)
    #...
end

という形で、ほげほげを書かないといけないので無理と判断しました。Context内に

def method_missing(name, *args)
    #...
    if ret =~ /(%w+)=/
        return "#{ret} = #{args[0]}"
    end
    #...
end

と書けば呼ばれるのでは?と思ったのですが、

Ratex.generate{ v = r * i }

としても呼ばれません。

v = nil
Ratex.generate{ v = r * i }
p v #=> "r + i"

どうやらローカル変数と解釈されたようです。

終わりに

投稿が遅れてしまいました。前にもアドベントカレンダーに投稿させてもらったのですが、これも遅れてしまいました。遅刻は病気かもしれないと言う話がありますが、どうなんでしょう?

最初にも書きましたが自作のgemの解説をしているのはおそらく自分だけだと思います(そして大した技術じゃない)。それに加えて遅れるという、完全に浮いてしまうか!と思っていますが、アドベントカレンダーは今年が初めてなので、生温かくスルーしていただければと思います。

自分が作ったものをこういう場で解説するのは少し恥ずかしい///のですが、突っ走れということで気にしません。多分後で後悔すると思います。

Rubyの式をTeXの数式に変換するやつ作った

これはTeX & LaTeX Advent Calendar 2013の11日目の記事です。遅れてすみません。

Rubyの式を変換するってどういうことかというと

Ratex.generate{ f(x, y, t) == 2 * sin(pi / 4 * sqrt(x ** 2 + y ** 2) - pi / 2 * t) }

で、

$$f(x, y, t) = 2 \sin(\frac{\pi}{4} \sqrt{x ^{2} + y ^{2}} - \frac{\pi}{2} t)$$

f(x,y,t)=2sin(π4x2+y2π2t)

となるという事です。これってRubyの記事じゃね?と思われるかもしれませんが、まぁTeXつながりという事で。

インストール方法

Rubyをインストールしているならば、

gem install ratex

でインストール出来ます。インストールしていないかたはさようなら。

使い方

Ratex.generate{
    #式...
}

で、TeXのコードが返ってきます。

機能一覧

基本演算

+, -, *, /があります。Rubyのべき乗である、**は、^になります。演算子の優先順序はRubyと同じです。

puts Ratex.generate{ a + b - c * d / e ** f } #=> $$a + b - \frac{c d}{e ^{f}}$$

a+bcdef

=は諸事情により、==です。また、定数は先頭に:をつけてください(これも諸事情)。

puts Ratex.generate{ :V == :R * :I } #=> $$V = R I$$

V=RI

関数

関数の括弧はそのまんまです。呼び出す方(sinやsqrt)も、関数呼び出しのようにかけます。

puts Ratex.generate{ f(x, y) == sqrt(x ** 2 + y ** 2) }

f(x,y)=x2+y2

まとめ

ここまで書いといてなんですが、全然実用的ではありません。なので、こんなんじゃ使えねーよ!と思われる方は、fork(GitHub)して勝手に編集してください(ぉぃ

オイラーの等式

puts Ratex.generate{ e ** (i * pi) == -1 }

eiπ=1

2013年12月7日土曜日

HaskellのWebフレームワークYesodを使ってみる

Haskell Advent Calendar 2013 6日目の記事です

Haskell製のWebフレームワーク、Yesodを使ってみます。

Yesodとは、

Yesod is a Haskell web framework for productive development of type-safe, RESTful, high performance web applications.

らしいです。typesafeやhigh performanceはrubyなどの動的型付け言語とは違うところですね。

cabal install yesod-platform

でインストール出来ます。少し時間がかかるらしいので、待ちましょう。さらに、yesodコマンドを使うために、

cabal install yesod-bin

をしましょう。何故かパスが設定されないので、$HOME/.cabal/binを設定しましょう。

さて、作るものは、最近まどか☆マギカにはまっているのでソウルジェム測定サービスというものにしましょう。

> yesod init
Welcome to the Yesod scaffolder.
I'm going to be creating a skeleton Yesod project for you.

What do you want to call your project? We'll use this for the cabal name.

Project name: SoulgemCounter
Yesod uses Persistent for its (you guessed it) persistence layer.
This tool will build in either SQLite or PostgreSQL or MongoDB support for you.
We recommend starting with SQLite: it has no dependencies.

    s      = sqlite
    p      = postgresql
    pf     = postgresql + Fay (experimental)
    mongo  = mongodb
    mysql  = MySQL
    simple = no database, no auth
    url    = Let me specify URL containing a site (advanced)

So, what'll it be? s
That's it! I'm creating your files now...

---------------------------------------

                     ___
                            {-)   |\
                       [m,].-"-.   /
      [][__][__]         \(/\__/\)/
      [__][__][__][__]~~~~  |  |
      [][__][__][__][__][] /   |
      [__][__][__][__][__]| /| |
      [][__][__][__][__][]| || |  ~~~~
  ejm [__][__][__][__][__]__,__,  \__/


---------------------------------------

The foundation for your web application has been built.


There are a lot of resources to help you use Yesod.
Start with the book: http://www.yesodweb.com/book
Take part in the community: http://yesodweb.com/page/community


Start your project:

   cd SoulgemCounter && cabal sandbox init && cabal install && yesod devel

ブロックを積んでるAAがかわいいw

SoulgemCounterに移動して、yesod develと打てばサーバーが起動します。

enter image description here http://localhost:3000 にアクセスすると、なんか名言っぽいのが切り替わっています。The application is’t built.らしいです。ログには

cabal: At least the following dependencies are missing:
http-conduit ==2.0.*, persistent-sqlite ==1.2.*, wai-extra ==2.0.*

とありました。調べてみると、どうやら入れなおしたほうがいいみたいです。

Yes, Yesod! - みょんさんの。を参考にしつつ環境を入れなおしているうちにどうやらソウルジェムが穢れてきたようです。(てか日付超えちゃっているし

というわけで中途半端な所で終わってしまいました。また余裕ができた時にリトライするかも

リンク

http://www.yesodweb.com/
本家
http://yannesposito.com/Scratch/en/blog/Yesod-tutorial-for-newbies/
マトリックス

2013年12月2日月曜日

YAMLでプログラムを書いてみる

キーと値を持ついわゆる連想配列にプログラムを書こうというよくわからん言語を考えてみた。名前は、実行可能なhashでXASH(eXecutable hASH)。今、Rubyで組んでる。

例えばFizzBuzzをこんな感じで書ける(予定)

#YAMLで書いた
for: [1..10,
    do: [[i],
        case: [
            [$i, mod, 15, ==, 0], do: [ puts: FizzBuzz ],
            [$i, mod, 15, ==, 0], do: [ puts: Fizz ],
            [$i, mod, 5, ==, 0], do: [ puts: Buzz ],
            do: [ puts: $i ]
        ]
    ]
]

今後仕様が変わる可能性があるが、全部式にしようと思う。なので、ifを始めとする構文も関数にしようとしている。

今、試験勉強期間なのであまりガッツリ実装できないが、一応for、lambda式あたりはできた

つまり、これがちゃんと動く。

- for: [1..10,
    do: [[i],
        print: [[$i, ', ']]
    ]
]

- puts: 

#Python style
- for:
    - 1..10
    - do: 
        - [i]
        - print: [[$i, ', ']]

- puts:

- for: [ array: [Ruby, Python, Perl] ,
    do: [[i],
        print: [[$i, ', ']]
    ]
]

- puts: 

- for:
    - object: { name: long_long_float, age: 9999 }
    - do:
        - [k, v]
        - print: [[$k, ' => ', $v]]
        - puts: 

- puts: 

- for: [hello,
    do: [[c],
        print: [[$c, ', ']]
    ]
]

出力

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
Ruby, Python, Perl, 
name => long_long_float
age => 9999

h, e, l, l, o, 

前に、Cuickというプログラミング言語を作ったことがあるが、処理の中心がパースだった(パース→AST木作る→C++コードで出力)。今回は文法はYAMLなのでパースは一瞬で楽だが、プログラマにやや易しくないorz…