prolog
『犯罪者プロファイリング入門』なんてのを読んだ。この所、プロファイリングブームですから。 この本、警察庁科学警察研究所の方と科捜研の方の共著になるもの。
この本で、科捜研ってのは、各道府県にある事を恥ずかしながら初めて知りましたよ。 科捜研の女なんて番組は真面目に見てないものですから。
科捜研には、文書鑑定をやる文書係りとか化学係りとか、ポリグラフ(嘘発見器)を担当する 係りとかが有るそうな。勿論、嘘発見器にかける質問票とかは、科捜研の係りの人が捻り出すそうです。
犯罪者プロファイリングにはFBI流とヨーロッパで発達した流派に大きく分かれるとか。 FBI流は、いわゆる職人派。ヨーロッパ流は、現場の捜査官と心理担当のコンビらしい。 日本の警察は、ヨーロッパ流を取り入れてるとの事。
勿論、コンピュータの力も取り入れている。犯罪データベース。これを使って類型の犯罪から いろいろと犯人の輪郭を絞って行く。
データベースにどんなデータが登録されているか? 通り魔DBの例が載ってた。 大きく分けて、被疑者(犯人)情報、犯行場面、被害者情報。被疑者の詳細は、性別、年齢、社会的地位(職業)等。 犯行場面では、場所(路上、屋内、公園等)、制圧(暴力、凶器、恫喝等)、天候等が保管される。 被害者情報は項目として被疑者と同じものが並んでいるようだ。
場所と被害者からマッチする被疑者像を検索するんだな。もう、統計処理ですよ。 統計処理と言っても、大きく分けて2つの方式になるとか。教師つき学習と教師無し学習。 前者では、線形判別学習とか決定木、ニューラルネット等。後者では、主成分分析とか、階層的クラスタリング とか、自己組織マップとかの方法が有るらしい。
こうなると統計言語Rの出番かと思うんだけど、警察に方々にはRなんて認知されていないみたい。 統計ソフトと言えばSPSSでしょうと言う訳。そして、Visual Mining Studioとかを使うらしい。 このマイニングツールって、数理システムの製品なのね。
数理システムと言えば、昔行った事があるぞ。そう、アレグロ 顧問リスプのセミナーが 有った時にね。そして件のソフトの資料を頂いた覚えがある。こういうのには顧問リスプが うってつけなんだろうね。何たって、顧問ですから。。。
まてまて、まだまだあるぞ、prologが。こいつは推論する事にかけちゃ天下一品さ。
elixir
日経Linuxでmatzさんがjulia等と共にelixir なんてのを薦めていたように思うんだけど、これってどうよ? えっ、そっちの方じゃなくて elixir erlang なんて検索語で出て来るelixirですってば。 嗚呼、びっくりしたなあ。まあ、 なぜElixirなのかなんてのが、 歌舞伎座で講演されるぐらいですから。。。やっとかないと今の流行から取り残されますよ。 歌舞伎座の人も頑張ってますよ。
なんだか、これを作った人がerlangってprologっぽくね? 今更prologなんて流行らないよってんで、 rubyに似せて作ったとか。ならば、おいらも首を突っ込んでみるかな。幸い、fedoraにパッケージが 有ったので、直ぐに入ったよ。
Installed: elixir.noarch 0:0.12.2-2.fc20 Dependency Installed: erlang-asn1.i686 0:R16B-03.1.fc20 erlang-compiler.i686 0:R16B-03.1.fc20 erlang-crypto.i686 0:R16B-03.1.fc20 erlang-erts.i686 0:R16B-03.1.fc20 erlang-hipe.i686 0:R16B-03.1.fc20 erlang-inets.i686 0:R16B-03.1.fc20 erlang-kernel.i686 0:R16B-03.1.fc20 erlang-mnesia.i686 0:R16B-03.1.fc20 erlang-public_key.i686 0:R16B-03.1.fc20 erlang-runtime_tools.i686 0:R16B-03.1.fc20 erlang-ssl.i686 0:R16B-03.1.fc20 erlang-stdlib.i686 0:R16B-03.1.fc20 erlang-syntax_tools.i686 0:R16B-03.1.fc20
ごっそりとerlang陣営を引き連れてきたぞ。で、rubyのirb相当がiexって事で用意されてた。一応、 起動確認をば。
[sakae@fedora ~]$ iex Erlang R16B03 (erts-5.10.4) [source] [async-threads:10] [hipe] [kernel-poll:false] Interactive Elixir (0.12.2) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution a
この後何をしたら良いんでしょうか? 作ったスクリプトは、elixirから起動するも良し、 下記のコンパイラを使ってerlangVMのクラスファイルって言うかビームファイルに落として おいて、erlangから起動するも良し、自由にどうぞって訳だな。
[sakae@fedora ~]$ elixirc -h Usage: elixirc [elixir switches] [compiler switches] [.ex files] -o The directory to output compiled files --no-docs Do not attach documentation to compiled modules --no-debug-info Do not attach debug info to compiled modules --ignore-module-conflict --warnings-as-errors Treat warnings as errors and return non-zero exit code --verbose Print informational messages. ** Options given after -- are passed down to the executed code ** Options can be passed to the erlang runtime using ELIXIR_ERL_OPTS
何やら外国ではelixir本も出てるようなんで、一定の市民権を獲得してるんでしょうな。 市民権と言えば、スクリプトの中にドキュメントを埋め込む事が推奨されてて、 Documentation as first-class citizenって具合に誇らしげに宣伝してた。
これ、古くは Lispに、新しくはPythonやclojureにも有るね。起動が遅くなってもいいから、ちゃんとドキュメントを 書いておこうよって、文芸的プログラミング推進派。gaucheは、速く起動させる為に、あえて 一級市民には入れなかったとか。人それぞれで面白い。
prolog
上でerlangがprologの影響を受けたって書いたけど、ならば基礎の基礎からだな。ここで 『7つの言語の7つの世界』なんて本を思い出したように取り出してみた。そしたら、erlangも prologの収録されてましたよ。
某板では、こんな発言があるな。
Erlangのオリジナルは論理型言語から出発しているが、 その言語とは(Concurrent PrologやGHCといった)並列論理型言語のことであり、 (いわゆるPrologと呼ばれる)逐次論理型言語が持つバックトラック機能は失われている だから、逐次Prologに関する知識の多くはErlangでは役に立たないと考えた方がいい Erlangの外観が逐次Prolog(いわゆるフツーのPROLOG)と似ているからといって、 惑わされてはいけない.
prologって流行のオブジェクト嗜好とも関数型言語とも違う、独立峰だ。これはもう登って、 経験値を高めておかないとな。かの本をざっと流し読みしたよ。そして、何か適当な本が 無いかと探ってみると、、昔はブームがあって山のように出版されてたようだけど、もうみんな絶版。 手に入らない。で、Webに無いかと探してみると、
国語ってのが気に入りましたよ。で、水練じゃつまんないんで、処理系を入れて動かしてみる。 取り合えず、FreeBSD上でね。
gprolog
GNUのやつとswiplが有ったけど、GNUのやつをチョイスしてみた。本山は The GNU Prolog web siteで、経典と言うか Manualが有れば、何とかなるだろう。
入れたはいいけど、どんなコマンドが有るの?
[sakae@fb10 ~]$ pkg info -l gprolog gprolog-1.4.1: /usr/local/bin/fd2c /usr/local/bin/gplc /usr/local/bin/gprolog /usr/local/bin/hexgplc /usr/local/bin/ma2asm /usr/local/bin/pl2wam /usr/local/bin/wam2ma :
いろいろあるけど雰囲気からしてgprologってのがメインっぽい。で、 emacsから使いたい場合は、こうするようだ。
(setq auto-mode-alist (append '(("\\.pl" . prolog-mode)) auto-mode-alist)) (setq prolog-program-name "/usr/local/bin/gprolog") ;; (setq prolog-consult-string "[user].\n")
こんな風に書いておくだけで良い。emacsには既にコードが含まれているからね。 簡単な使い方が prolog on emacs 有ったぞ。swlplだけど、一緒だろう。
まずは、7つの言語に習ってと。
[sakae@fb10 ~]$ gprolog GNU Prolog 1.4.1 By Daniel Diaz Copyright (C) 1999-2012 Daniel Diaz | ?- like(sakae, ruby). uncaught exception: error(existence_error(procedure,like/2),top_level/0)
いきなり質問したって、そんなのprologは知る訳ないわな。普通は、こういう定義を ファイルに書いておいて、それを読み込んで(覚えさせて)から、質問するのが人の道。 でも、せっかちな人用に、抜け道が用意されてる。
| ?- [user]. compiling user for byte code... like(sakae, ruby). like(sakae, scheme). like(matz, ruby). like(shiro, scheme). CTRL-d で、復帰する user compiled, 5 lines read - 479 bytes written, 97160 ms yes
userって言う仮想ファイルから読み込みます。終わりは、EOFって事ね。これって、かの昔、 Bill Joyさんが、cat - >hoge.c とかやって、いきなり端末からソースを起したのに 等しい行為です。凡人は慎みましょう。
上のは、まあ意味分かるよね。現実をprologに覚えて貰ったんだ。そしておもむろに
| ?- like(matz, ruby). yes | ?- like(matz, scheme). no
matzさんがrubyを好きなのはうなずけるけど、schemeは嫌いってどうよ? と、早合点しては いけません。noは、否定の意味と、そんな事は知らんって意味が有るんですな。と、かのWeb に説明が有ったけど、両者はどうやって見分けるの? 教えて偉い人。
| ?- like(Who, scheme). Who = sakae ? ; Who = shiro ? ; no
今度は、誰がschemeを好きかって質問。最初の回答の後にセミコロンを入力すると、続いて 検索してくれるよ。
次なる例は例によって、fact。
fact(0, 1). fact(X, Sum) :- X > 0, X1 is X - 1, fact(X1, Sum1), Sum is X * Sum1.
fact(0)の答えは1てすって(再帰の)基底を宣言。それ以外は、次のルールが評価される。 Xが零より大きかったら X1 is X - 1しなさい(普通の言語でX1=X-1と書きたいけど、 prologの場合は、我慢我慢)。間にカンマが入っているけど、これは andね。 そして再帰、
上記をfact.plってファイルにsave。おもむろに、run-prologすると画面が割れて、gplologが 顔を出す。
GNU Prolog 1.4.1 By Daniel Diaz Copyright (C) 1999-2012 Daniel Diaz | ?- [fact]. compiling /usr/home/sakae/src/pl/fact.pl for byte code... /usr/home/sakae/src/pl/fact.pl compiled, 3 lines read - 807 bytes written, 24 m\s yes | ?- fact(10,Ans). Ans = 3628800 ? yes
これで答えが求まった。間違っても1引数の関数として実行しないように。間違うと
| ?- fact(5). uncaught exception: error(existence_error(procedure,fact/1),top_level/0)
文句を言われる。システムが、どういう風にコードを認識してるかは
| ?- listing. % file: /usr/home/sakae/src/pl/fact.pl fact(0, 1). fact(A, B) :- A > 0, C is A - 1, fact(C, D), B is A * D.
こんな具合に確認出来る。大文字で始まるのは、変数だよ。それ以外はアトムって言うから 分解出来ないっていう建前なんだな。
どんな風に計算が進んで行くかは、traceしてみれば分かる。
| ?- trace. The debugger will first creep -- showing everything (trace) yes {trace} | ?- fact(2,X). 1 1 Call: fact(2,_17) ? 2 2 Call: 2>0 ? 2 2 Exit: 2>0 ? 3 2 Call: _114 is 2-1 ? 3 2 Exit: 1 is 2-1 ? 4 2 Call: fact(1,_139) ? 5 3 Call: 1>0 ? 5 3 Exit: 1>0 ? 6 3 Call: _191 is 1-1 ? 6 3 Exit: 0 is 1-1 ? 7 3 Call: fact(0,_216) ? 7 3 Exit: fact(0,1) ? 8 3 Call: _244 is 1*1 ? 8 3 Exit: 1 is 1*1 ? 4 2 Exit: fact(1,1) ? 9 2 Call: _17 is 2*1 ? 9 2 Exit: 2 is 2*1 ? 1 1 Exit: fact(2,2) ? X = 2 ? yes {trace}
traceの解除はnotraceだ。他にもspyなんてのが有るぞ。
gplc
gprologはコンパイラーも備わっていて、コードをコンパイルして、実行プログラムを作れるそうだ。 早速やってみる。下記は某Webから頂いてきた。日本人なら誰でも知っている 歳を取らない不思議な家族、サザエさん一家の家系図。
% child(X, Y):X は Y の子供である child(sazae, namihei). child(sazae, fune). child(katsuo, namihei). child(katsuo, fune). child(wakame, namihei). child(wakame, fune). child(tarao, sazae). child(tarao, masuo). % P が成り立てば true を、成り立たなければ fail を X に代入 istrue(P, X) :- P, !, X = true; X = fail. :- initialization(sazae_family). sazae_family :- % masuo は namihei の子供か? istrue(child(masuo, namihei), X0), write(X0), nl, % fune の子供を列挙する findall(X1, child(X1, fune), List1), write(List1), nl, % tarao を子供にもつ者(つまり、taraoの親)を列挙する findall(X2, child(tarao, X2), List2), write(List2), nl, % child(X, Y) が成り立つすべての組 (X, Y) を列挙する findall([X3, Y3], child(X3, Y3), List3), write(List3), nl.
実行プログラムが自動的に走るようにイニシャライザーの定義を書いておくのと、gprologの replに落ち込まないようにコンパイラーに指示を与える。
[sakae@fb10 ~/src/pl]$ gplc -v sazae.pl --no-top-level Prolog compiler (GNU Prolog) 1.4.1 By Daniel Diaz Copyright (C) 1999-2012 Daniel Diaz GNU Prolog comes with ABSOLUTELY NO WARRANTY. This is free software; see the source or the file named COPYING for copying conditions. Path used: /usr/local/gprolog-1.4.1 *** Compiling --- file: sazae.pl pl2wam -o /tmp/gplcqDVwGb.wam sazae.pl wam2ma -o /tmp/gplcG6GTcd.ma /tmp/gplcqDVwGb.wam delete /tmp/gplcqDVwGb.wam ma2asm -o /tmp/gplcWzsgJe.s /tmp/gplcG6GTcd.ma delete /tmp/gplcG6GTcd.ma as -o /tmp/gplc8QYXyb.o /tmp/gplcWzsgJe.s delete /tmp/gplcWzsgJe.s *** Linking gcc46 -march=i486 -m32 -fno-strict-aliasing -Wl,-rpath=/usr/local/lib/gcc46 -L/usr/local/lib/gcc46 -o sazae /tmp/gplc8QYXyb.o /usr/local/gprolog-1.4.1/lib/all_pl_bips.o /usr/local/gprolog-1.4.1/lib/all_fd_bips.o /usr/local/gprolog-1.4.1/lib/libbips_fd.a /usr/local/gprolog-1.4.1/lib/libengine_fd.a /usr/local/gprolog-1.4.1/lib/libbips_pl.a /usr/local/gprolog-1.4.1/lib/libengine_pl.a /usr/local/gprolog-1.4.1/lib/liblinedit.a -lm delete /tmp/gplc8QYXyb.o
コンパイルの過程が良く分かるように-vオプションを付けてみた。マシンのアーキテクチャが i486なんて、時代がかった奴でも動くようにしてくれてるのか。
[sakae@fb10 ~/src/pl]$ ./sazae fail [sazae,katsuo,wakame] [sazae,masuo] [[sazae,namihei],[sazae,fune],[katsuo,namihei],[katsuo,fune],[wakame,namihei],[wakame,fune],[tarao,sazae],[tarao,masuo]]
旨く実行出来た。
[sakae@fb10 ~/src/pl]$ ldd ./sazae ./sazae: libm.so.5 => /lib/libm.so.5 (0x28111000) libc.so.7 => /lib/libc.so.7 (0x28133000)
いたって平凡なライブラリィーしか使っていない。
WAM
GNU PrologのHP上に、どやって動くのって説明が載ってて、そこにWarren Abstract Machine なんて名前が出てくる。ワーナーさんの考えた機械なんだな。この機械はprologを実行する のに都合よく考えて作ったとな。丁度、Schemeを実行するのに都合が良い機械、SECDマシン が有ったように。
SECDを復習すると、スタック、環境、コード、データって事だったな。そなら、Prologは如何に? ぐぐったら、 (D.Warrenの抽象Prolog命令セット, WAM) なんてのが出てきた。
gprolog from src
FreeBSDでは楽して(手抜きして)、pkgから入れちゃったけど、やはりオイラーの流儀に反する。 という事で、万次郎Linuxにはソースから入れてみた。1分で完了。ログを取ったので、 どんな風にコンパイルしてるか見てみた。
cd EnginePl; make config (cd ../TopComp; make gplc) . ./SETVARS;for i in EnginePl TopComp Wam2Ma Ma2Asm Linedit BipsPl Pl2Wam Fd2C EngineFD BipsFD;do (cd $i; make) || exit 1; done;\ (cd TopComp; make top-level) || exit 1;\ (cd Pl2Wam; make stage2)
configして、gplcを作って、コンパイルして、plのソースコードをwamに落としてコンパイルって 手順だな。
折角なのでソースも見とくと、上で出てきたWAMの仕様が、EnginePl/wam_*.h あたりで出ていた。 そのうちの、wam_archi.hに使うRegの定義が出てたぞ。
#define Reg(i) (((i)==0) ? (WamWord) TR : \ ((i)==1) ? (WamWord) B : \ ((i)==2) ? (WamWord) H : \ ((i)==3) ? (WamWord) HB1 : \ ((i)==4) ? (WamWord) CP : \ ((i)==5) ? (WamWord) E : \ ((i)==6) ? (WamWord) CS : \ ((i)==7) ? (WamWord) S : \ ((i)==8) ? (WamWord) STAMP : \ ((i)==9) ? (WamWord) BCI : \ (WamWord) LSSA)
そんじゃ、Linuxでもコンパイルテストをしてみる。今度は、コンパイラードライバーに 中間ファイルを消すなオプションを付けておいた。
[sakae@manjaro pl]$ gplc --no-del-temp-files -v sazae.pl Prolog compiler (GNU Prolog) 1.4.4 By Daniel Diaz Copyright (C) 1999-2013 Daniel Diaz GNU Prolog comes with ABSOLUTELY NO WARRANTY. This is free software; see the source or the file named COPYING for copying conditions. Path used: /usr/local/gprolog-1.4.4 *** Compiling --- file: sazae.pl pl2wam -o /tmp/gplc7j3wGb.wam sazae.pl wam2ma -o /tmp/gplc4tWTcd.ma /tmp/gplc7j3wGb.wam ma2asm -o /tmp/gplc1DPgJe.s /tmp/gplc4tWTcd.ma as --32 -o /tmp/gplcUBtYyb.o /tmp/gplc1DPgJe.s *** Linking gcc -march=pentiumpro -m32 -fno-strict-aliasing -o sazae /tmp/gplcUBtYyb.o /usr/local/gprolog-1.4.4/lib/all_pl_bips.o /usr/local/gprolog-1.4.4/lib/all_fd_bips.o /usr/local/gprolog-1.4.4/lib/top_level.o /usr/local/gprolog-1.4.4/lib/debugger.o /usr/local/gprolog-1.4.4/lib/libbips_fd.a /usr/local/gprolog-1.4.4/lib/libengine_fd.a /usr/local/gprolog-1.4.4/lib/libbips_pl.a /usr/local/gprolog-1.4.4/lib/libengine_pl.a /usr/local/gprolog-1.4.4/lib/liblinedit.a -lm
環境に馴染むようにコンパイルすると、そのパソコンの石用にコンパイルしてくれてる。やっぱり 自前でコンパイルしておくのに限るな。
で、今更だけど、PLファイルがWAM用のコードになって、WAM用のアセンブラMAになって、 それが糞石用のコードに落ちて、最後はオブジェクトファイルになる。そしてそれを、 必要なライブラリィーとリンクして、実行コードにするとな。長い旅だのう。
そんじゃ、WAM用に変換されたやつ
predicate(child/2,2,static,private,monofile,global,[ switch_on_term(6,1,fail,fail,fail), : label(21), get_atom(tarao,0), get_atom(masuo,1), proceed]). predicate(istrue/2,12,static,private,monofile,global,[ pragma_arity(3), get_current_choice(x(2)), get_variable(x(3),1), put_value(x(2),1), put_value(x(3),2), execute('$istrue/2_$aux1'/3)]).
頑張って、読もうと思えば読めるな。次はMAコードな。
pl_code global X0_child__a2 call_c fast Pl_Switch_On_Term_Var_Atm(&Lpred1_6,&Lpred1_1) jump_ret Lpred1_1: call_c fast Pl_Switch_On_Atom(st(0),4) jump_ret : pl_code global X0_sazae_family__a0 call_c fast Pl_Allocate(4) call_c fast Pl_Put_Structure_Tagged(fn(0)) move_ret X(0) call_c fast Pl_Unify_Atom_Tagged(ta(6)) fail_ret call_c fast Pl_Unify_Atom_Tagged(ta(1)) fail_ret call_c fast Pl_Put_Y_Variable(&Y(0)) move_ret X(1)
もう、何が何だか? そして、見ると目が腐ると言う糞石用のやつ。
X0_child__a2: movl $Lpred1_6+0,%eax movl $Lpred1_1+0,%edx call Pl_Switch_On_Term_Var_Atm jmp *%eax Lpred1_1: movl st+0,%eax movl $4,%edx call Pl_Switch_On_Atom jmp *%eax :
もう、大変。