# RubyからHaskellのコードを呼ぶ邪道な方法(ベンチマーク付) _published: 2009/02/14_ ![alt](http://b.hatena.ne.jp/entry/image/http://d.hatena.ne.jp/shunsuk/20090214/1234605998) RubyにはRubyの、HaskellにはHaskellの得意分野があるわけでして。RubyからHaskellのコードを呼びたいという需要はあると思います。Cで皮を被せれば呼べるようになる気がしますが、調べるのに時間がかかりそうなので保留。とりあえず、邪道な方法を思いついたので試してみます。 まずは、Haskellのコード。階乗を求める関数です。 ```haskell import System main = do args <- getArgs print $ fact $ stoi $ head args stoi :: String -> Integer stoi s = (read s :: Integer) fact :: Integer -> Integer fact n = factItter n where factItter n | n == 0 = 1 | otherwise = n * factItter (n - 1) ``` Haskell初心者なので、Haskellっぽくなかったらスミマセン。 コンパイル。 ```sh % ghc fact.hs -o fact ``` 普通に実行可能ファイルを作ります。 これ呼び出すRubyのコード。 ```ruby def fact(n) `./fact #{n}`.to_i end puts fact(ARGV[0].to_i) ``` バッククォートについては、こちら。 - [Rubyのバッククォート文字列でシェルコマンド実行 - このブログは証明できない。](http://d.hatena.ne.jp/shunsuk/20081223/1230040060) コマンドの実行結果を文字列で取得できます。 これで、RubyからHaskellのコードを呼び出すことができました。邪道でしょ? ベンチマーク。引数を変えてやってます。pure haskellはコンパイルしたファイルをそのまま実行。ruby to haskellが上記のRubyスクリプト。pure rubyは下のコード。 ```ruby def fact(n) if n == 0 then 1 else n * fact(n - 1) end end puts fact(ARGV[0].to_i) ``` | | 100 | 1,000 | 10,000 | 100,000 | | --------------- | ---- | ----- | ---------------- | ------- | | pure haskell | 0.00 | 0.00 | 0.24 | over 10 | | ruby to haskell | 0.01 | 0.02 | 2.42 | over 10 | | pure ruby | 0.01 | 0.03 | SystemStackError | | あ、pure rubyだとスタックがオーバーフローしたっぽい。再帰じゃなくてループに変更。 ```ruby def fact(n) a = 1 (1..n).each {|x| a *= x} a end puts fact(ARGV[0].to_i) ``` Haskellのコードも修正しないと、比較になりませんんね。末尾再帰にすればいいのかな? ```haskell import System main = do args <- getArgs print $ fact $ stoi $ head args stoi :: String -> Integer stoi s = (read s :: Integer) fact :: Integer -> Integer fact n = factItter n 1 where factItter n x | n == 0 = x | otherwise = factItter (n - 1) (x * n) ``` 改めて、ベンチマーク。 | | 100 | 1,000 | 10,000 | 100,000 | | --------------- | ---- | ----- | ------ | ------- | | pure haskell | 0.00 | 0.00 | 0.27 | over 10 | | ruby to haskell | 0.01 | 0.02 | 2.46 | over 10 | | pure ruby | 0.01 | 0.02 | 1.67 | over 10 | ruby to haskell遅え! で、みなさんお気づきかと思いますが。文字列をやりとりしてるのがマズそう。10,000の階乗は660桁です。 出力結果として常に0を表示するように修正。 | | 100 | 1,000 | 10,000 | 100,000 | | --------------- | ---- | ----- | ------ | ------- | | pure haskell | 0.00 | 0.00 | 0.00 | 0.00 | | ruby to haskell | 0.01 | 0.01 | 0.01 | 0.01 | | pure ruby | 0.01 | 0.02 | 0.75 | over 10 | 予想通り!そりゃ速いよね。コンパイルしてるし。いや、このコードがコンパイルで差がでるとは思えない。このへんか。 - [Rubyはなぜ遅いのか?Ruby 1.9は速いのか? - このブログは証明できない。](http://d.hatena.ne.jp/shunsuk/20081023/1224757522) ここから、おまけ。リストを返したい時は? ```haskell import System main = do args <- getArgs print $ fib $ stoi $ head args stoi :: String -> Int stoi s = (read s :: Int) fib :: Int -> [Int] fib n = fibItter n 0 [1] where fibItter n a xs | n == 0 = reverse xs | otherwise = fibItter (n - 1) (head xs) ((head xs + a):xs) ``` evalしちゃいなよ! ```ruby def fib(n) eval(`./fib #{n}`) end puts fib(ARGV[0].to_i) ``` 通常のライブラリのように、関数をひとまとめにできないのが欠点です。文字列が長くなると遅くなるし。でも、HaskellはIO周りがキツイので、そこをRubyで書けると便利だと思うのです。あと、この方法なら、HaskellじゃなくてもOKですね。