たらいまわしといえば Lisp

This entry was posted by on Saturday, 8 April, 2006
> >http://blog.livedoor.jp/dankogai/archives/50447103.html > たらいまわし関数といえば別名を竹内関数といい、「Lispの神様」こと竹内郁雄先生が考案した「とにかく再帰的呼出しがある関数」として有名です。それなのに Lisp に対して言及がないのは何事かと。特に Scheme では promise (遅延評価機構)が言語仕様に組込まれているわけですから使うと速いんじゃないかと思うわけです。 > >ああでもおれ Schemer じゃないから、こういう「引数があるときのデフォルト値の適切な表現」がよくわからんです……。マニュアル首っぴきでとりあえず次のようなものを書いてみた。処理系は Gauche。 > >#!/usr/bin/gosh

(define (tak x y z)
(if (<= (force x) (force y))
(force y)
(tak (delay (tak (- (force x) 1) y z))
(delay (tak (- (force y) 1) z x))
(delay (tak (- (force z) 1) x y)))))

(let-optionals*
(map string->number *argv*) ((x 10) (y 5) (z 0))
(format #t "tak(~A, ~A, ~A) = ~A\n" x y z (tak x y z))) > >% time ./tak.pl 100 50 0
tak(100, 50, 0) = 100
./tak.pl 100 50 0 0.38s user 0.01s system 96% cpu 0.406 total
% time ./tak.rb 100 50 0
tak(100, 50, 0) = 100
./tak.rb 100 50 0 0.44s user 0.02s system 95% cpu 0.485 total
% time ./tak.scm 100 50 0
tak(100, 50, 0) = 100
./tak.scm 100 50 0 0.06s user 0.01s system 86% cpu 0.080 total
> >かように Scheme は滅茶苦茶速い。ほかにもたとえば OCaml にも遅延評価機構はある。こんな感じ。 > >open Lazy
open Array
open Sys
let rec tak x y z =
if force x <= force y
then force y
else tak (lazy (tak (lazy (force x - 1)) y z))
(lazy (tak (lazy (force y - 1)) z x))
(lazy (tak (lazy (force z - 1)) x y))

let _ =
let (x, y, z) =
if length argv = 4
then (int_of_string argv.(1), int_of_string argv.(2), int_of_string argv.(3))
else (10, 5, 0)
in
Printf.printf "tak(%d, %d, %d) = %d\n" x y z (tak (lazy x) (lazy y) (lazy z)) > >% time ocaml ./tak.ml 100 50 0
tak(100, 50, 0) = 100
ledit ocaml ./tak.ml 100 50 0 0.08s user 0.01s system 81% cpu 0.121 total
% time ./tak.mlc 100 50 0
tak(100, 50, 0) = 100
./tak.mlc 100 50 0 0.02s user 0.00s system 70% cpu 0.037 total
% time ./tak.mlx 100 50 0
tak(100, 50, 0) = 100
./tak.mlx 100 50 0 0.00s user 0.00s system 102% cpu 0.007 total
> >mlc はバイトコードコンパイル、 mlx はネイティブコードコンパイルした結果。いやー速い速い。 > >OCaml にしろ Scheme にしろ、言語の処理機構として遅延評価機構が取り込まれているということが大きいのだろう。 Ruby や Perl や Python で汎用のテーブル的なデータ構造を使い、メモ化をエミュレートすることもできるが、どうしてもオーバヘッドが出る。それにコンパイラの最適化も難しい。……ってあたりの差だと思う。たぶん。 > >ちなみに Haskell はコンパイラだが、スクリプトっぽく使うこともできる。 > >% time runghc tak.hs 100 50 0
tak(100, 50, 0) = 100
runghc tak.hs 100 50 0 0.88s user 0.04s system 97% cpu 0.950 total
> >やっぱり Haskell のコンパイル時間は物凄い。 > >ところで、みんなあんまりにも速いくて差が小さいから、引数を大きくしてみた。 > >% time ./tak.pl 500 250 0
tak(500, 250, 0) = 500
./tak.pl 500 250 0 6.20s user 0.08s system 97% cpu 6.439 total
% time ./tak.rb 500 250 0
tak(500, 250, 0) = 500
./tak.rb 500 250 0 6.66s user 0.34s system 99% cpu 7.043 total
% time ./tak.py 500 250 0
tak(500, 250, 0) = 500
./tak.py 500 250 0 3.54s user 0.05s system 98% cpu 3.658 total
% time ./tak.mlx 500 250 0
tak(500, 250, 0) = 500
./tak.mlx 500 250 0 0.07s user 0.00s system 87% cpu 0.080 total
% time ./tak.mlc 500 250 0
tak(500, 250, 0) = 500
./tak.mlc 500 250 0 0.51s user 0.00s system 97% cpu 0.531 total
% time ocaml ./tak.ml 500 250 0
tak(500, 250, 0) = 500
ledit ocaml ./tak.ml 500 250 0 0.58s user 0.01s system 97% cpu 0.608 total
% time ./tak.scm 500 250 0
tak(500, 250, 0) = 500
./tak.scm 500 250 0 0.62s user 0.00s system 98% cpu 0.632 total
% time ./tak.hx 500 250 0
tak(500, 250, 0) = 500
./tak.hx 500 250 0 0.03s user 0.00s system 99% cpu 0.035 total
> >注: Python はよくわからないので、コードは
>こちら >から拝借しました。 > >おまけ。 > >% ls -l tak.*
-rw-r--r-- 1 mukai local 356 2006-04-08 17:36 tak.cmi
-rw-r--r-- 1 mukai local 1354 2006-04-08 17:36 tak.cmo
-rw-r--r-- 1 mukai local 343 2006-04-08 17:36 tak.cmx
-rw-r--r-- 1 mukai local 368 2006-04-08 17:36 tak.hi
-rw-r--r-- 1 mukai local 400 2006-04-08 17:36 tak.hs
-rwxr-xr-x 1 mukai local 431764 2006-04-08 17:36 tak.hx
-rw-r--r-- 1 mukai local 495 2006-04-08 17:36 tak.ml
-rwxr-xr-x 1 mukai local 40985 2006-04-08 17:36 tak.mlc
-rwxr-xr-x 1 mukai local 111688 2006-04-08 17:36 tak.mlx
-rwxr-xr-x 1 mukai local 461 2006-04-08 17:36 tak.pl
-rwxr-xr-x 1 mukai local 472 2006-04-08 17:36 tak.py
-rwxr-xr-x 1 mukai local 397 2006-04-08 17:36 tak.rb
-rwxr-xr-x 1 mukai local 370 2006-04-08 17:36 tak.scm
> >tak.hx と tak.mlx は strip していますよと。 >

Comments are closed.