haskell and pure (2)

昔々その昔、おいらが小学生だった頃、野外学習とかでドウランを下げて近くの山へ草を 取りに行った事がある。別に薬草になるような物を取ってきて、学費の足しにしようとか じゃなくて、取ってきて遊びましょって事。

理科室で、その草を分類してみましょって先生に言われ、適当に分類してたら、先生から 後でつっこまれ、それ以来植物なんて嫌いだーってトラウマになっちゃいましたね。 右も左も分からない小学校の低学年じゃ、意図が分からなかったんだろうなと思うよ。

雑草と言う名前の草は無い、と、高名な学者さんが言ったとか。

名前を付けるには、その特徴をしっかりと見極めておかなければならない。いろいろな 特徴を抽出して名前を検索する事になるんだけど、眼のつけどころを伝授してくれる 本が有ったので、遅ればせながら、トラウマ解消のために読んでみた。 植物の特徴を見分ける本

特徴って書いたけど専門用語では形質と言うそうだ。英語だとキャラクターだ。 植物の場合は、茎の形にはじまって茎の切口やら葉の形や葉の脈、、、花の色やら実の 形など、さまざまな眼のつけどころがある。

それぞれの形質について、数種に分類する。たとえば、茎の切口なら、(A)円形・その他、 (B)四角形、(C)三角形、(D)中空、(E)汁が出る、なんて具合にラフに分類。形質によっては、 どちら共判定出来ない事がある。そういう時には、AとBが候補ってしても良いし、不明って 事にしてもよい。(花と実を同時に観察出来る植物なんて、普通無いよね) これらをデータベースと照合すれば、たちどころに植物の名前が判明。

データベースって書いたけど、野外にパソコンってのもきついので、パンチカードですよ。 一致は、自分が打ち込んだ形質の穴が、全て見えるカードを見つけるって事で 行う。原始的だけど理にかなっているな。カードを作ってみようかな。

今流行りのpureで検索システムを作ろうとすると、pure-csvとかpure-sql3が使えるんで、、 データの永続化に悩みは無さそうだな。

pure-sockets

pureのライブラリーが簡単に入るか、試してみる。上で挙げたDB系でもいいんだけど、雪が 降ってきて野外観察には不向きなので来年まで持ち越し。お題を、pure-socketsにしてみる。sudo make install で入るんだ。*.soとそれを駆動する*.pureのペアが、/usr/local/lib/pureの下に配置された。 サンプルが2つ付いていたので、試してみた。

[sakae@pcbsd ~/src/pure/examples]$ (./dgram.pure server &) && ./dgram.pure client
client: localhost:5002
server: localhost:5001
client> map (*2) (1..10);
client: localhost:5002
server> map (*2) (1..10);
[2,4,6,8,10,12,14,16,18,20]
client> map (* 3) (1..100);
client: localhost:5002
server> map (* 3) (1..100);
val #<pointer 0x2a0f4000>
client> reverse (1..30);
client: localhost:5002
server> reverse (1..30);
[30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]
client> quit

欲張って、余り多くの結果を求めると、ポインターが返ってきます。この辺の仕組みは ソース嫁って事なんでしょうな。

blob x¶

    Stores the contents of the given expression as a binary object. The return
    value is a cooked pointer which frees itself when garbage-collected.

val p¶

    Reconstructs a serialized expression from the result of a previous
    invocation of the blob function.

読んだら、こういうのに出くわした。プロセスとかネットワーク間のデータの渡しに 使えって効能が書いてあった。

で、疑り深いおいらは、本当にパケットが流れているか、確認に走ってみました。えっと、Xは 上がっていないので、昔ながらのCUIオペコマンドで。(こっそり、man tcpdumpしたのは、 内緒ですよ)

[sakae@pcbsd ~]$ sudo tcpdump -i lo0 -X -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo0, link-type NULL (BSD loopback), capture size 65535 bytes
05:57:55.124616 IP 127.0.0.1.5002 > 127.0.0.1.5001: UDP, length 84
        0x0000:  4500 0070 1081 0000 4011 0000 7f00 0001  E..p....@.......
        0x0010:  7f00 0001 138a 1389 005c c321 009d 3287  .........\.!..2.
        0x0020:  7187 3573 5400 0000 0000 0000 4000 0000  q.5sT.......@...
        0x0030:  0000 0000 faff ffff 0000 0000 1300 0000  ................
        0x0040:  0000 0000 6d61 7020 282a 2032 2920 2831  ....map.(*.2).(1
        0x0050:  2e2e 3230 293b 0029 0000 0000 0000 0000  ..20);.)........
        0x0060:  0000 0000 0000 0000 0000 0000 99ed ffff  ................
05:57:55.129986 IP 127.0.0.1.5001 > 127.0.0.1.5002: UDP, length 380
           :

クライアントからサーバーへの計算依頼は、文字列で伝えているけど、ダウンストリームは、 バイナリーで返ってくるのね。納得しました。あ、違った、文字列はblobしても文字列 だったわい。ソースは丁寧に読みましょう。

マニュアル拾い読み

__show__ なんて、Pythonみたいだな。どんな事出来るの? 例が出てた。

  > using system;
  > __show__ x::double = sprintf "%0.6f" x;
  > 1/7;
  0.142857
  > __show__ x::int = sprintf "0x%0x" x;
  > 1786;
  0x6fa
  > using math;
  > __show__ (x::double +: y::double) = sprintf "%0.6f+%0.6fi" (x,y);
  > cis (-pi/2);
  0.000000+-1.000000i

こんな風に、出力のフォーマットを自分で定義出来る。よく使う定義は、.purercに書いて おけとな。標準に戻すには、clear __show__ すればよい。clearなんてコマンド、basicに 有ったなあ。懐かしいぞ。showなんてコマンドも機能豊富。

> show -h
show command help: show [options ...] [symbol ...]
Options may be combined, e.g., show -fg f* is the same as show -f -g f*.
-a  Disassembles pattern matching automata. Useful for debugging purposes.
-c  Print information about defined constants.
-d  Disassembles LLVM IR, showing the generated LLVM assembler code of a
    function. Useful for debugging purposes.
-e  Annotate printed definitions with lexical environment information
    (de Bruijn indices, subterm paths). Useful for debugging purposes.
-f  Print information about defined functions.
-g  Indicates that the following symbols are actually shell glob patterns
    and that all matching symbols should be listed.
-h  Print this list.
-l  Long format, prints definitions along with the summary symbol
    information. This implies -s.
-m  Print information about defined macros.
-p[flag] List only private symbols if flag is nonzero (the default),
    otherwise list only public symbols. List both private and public
    symbols if -p is omitted.
-s  Summary format, print just summary information about listed symbols.
-t[level] List only symbols and definitions at the given temporary level
    (the current level by default) or above. Level 1 denotes all temporary
    definitions, level 0 *all* definitions. If this option is omitted,
    it defaults to -t0 if any symbols are specified, -t1 otherwise.
-v  Print information about defined variables.
-y  Print information about defined types.

こういうのでも間に合わないときは、虫取り器の出番です。

debuggerを使えるようにするには、-g を付けて起動するんだな。その多 -vとかで、debugの レベルを変えられるとな。

> sq x = x * x ;
> break sq
> sq 5;
** [1] sq: sq x = x*x;
     x = 5
(Type 'h' for help.)
: h
Debugger commands:
a       auto: step through the entire program, run unattended
c [f]   continue until next breakpoint, or given function f
h       help: print this list
n       next step: step over reduction
p [n]   print rule stack (n = number of frames)
r       run: finish evaluation without debugger
s       single step: step into reduction
t, b    move to the top or bottom of the rule stack
u, d    move up or down one level in the rule stack
x       exit the interpreter (after confirmation)
.       reprint current rule
! cmd   execute interpreter command
? expr  evaluate expression
<cr>    single step (same as 's')
<eof>   step through program, run unattended (same as 'a')
: n
++ [1] sq: sq x = x*x;
     x = 5
     --> 25
25

後、便利な関数として、ans なんてのがある。最後に評価した結果がbindされているので 前の結果を使いたい時は、コピペ不用で、ans; ってするだけで良い。ocamlだかに有った it と同様な働きをしてくれる。

poor

マニュアルを眺め終わったら、pureのソースツリーに置いてある例を見てく。これって作者のサンタさん からのクリスマスプレゼントだと思うぞ。作者さんが作った言語の特徴をいかんなく発揮 出来るような例が集まっているんで、見逃してしまうなんて超もったいない。

examplesの直下にMakefileが置いてある、何かと思って開いてみれば、poorって事で 本も買えないような貧乏人よって事みたい。で、poor.cを見ると

/* Poor man's Pure interpreter. 2008-06-24 AG */
#include <stdio.h>
#include <stdlib.h>
#include <pure/runtime.h>

int main(int argc, char *argv[])
{
  pure_interp *interp = pure_create_interp(argc, argv);
  char buf[10000];
  if (!interp) return 1;
  fputs("? ", stdout); fflush(stdout);
  while (fgets(buf, sizeof(buf), stdin)) {
    pure_expr *x = pure_eval(buf);
    if (x) {
      char *s = str(x);
      printf("%s\n", s);
      pure_freenew(x); free(s);
    } else if (lasterr())
      fputs(lasterr(), stderr);
    fputs("? ", stdout); fflush(stdout);
  }
  puts("[quit]");
  return 0;
}

機能を削ぎ落とした、インタープリターの例。勿論、エンジンはpureのライブラリーを 拝借してるけど。。goshは、裏に隠れてるgaucheのライブラリーを利用するための フロントエンジンってのとよく似てる。

[sakae@manjaro examples]$ ./poor
? x = 123;
()
? x;
123
? x = 456;
warning: rule never reduced: x = 456;
()
? x;
123
? x + y ;
123+y
? [quit]

変数への再代入はだめよってのは、しっかりと受け継がれている。定義してない変数を 参照しても、文句を言ってこないぞ。何で? まあ、そんな事考えるのは予想。

上で肝になるのは、runtime.hだな。取り合えずこいつを見ておけ。ついでに、ソースツリーを グローバル化しておけ。後は、マウスでツンツンするだけで、pureの秘密に迫れるぞ。

globalして、Webから見られるようにしたのはいいけど、大量に残されているコメントの 配色がpureな緑で、眼がチカチカする。電飾みたいで耐えられない。どこをつつけば 色を変えられるかと思ったら、そういう装飾系は、style.cssにまとめられているのね。 暗い緑にして、斜めを止めにしたら、大分見やすくなったぞ。

Sudoku

数独、日本発のゲームと言うか、日本のパズルメーカーであるニコリの発明品。 ナンプレと日本では言っているが、世界的には、sudokuが共通語。pureで書かれた、 回答器が有った。

/* Sudoku example by Peter Bernschneider. */

/* This is an example Sudoku solver inspired by the Queens example which
   terminates in reasonable time. m1 is ZEIT-Online Sudoku of Dec 6th 2009.
   Interactive usage example: sudoku1 m1; */

using matrices;
using system;

m1 = {0,0,3,9,0,2,0,0,0;
      0,0,0,0,0,0,2,3,1;
      0,0,0,0,0,3,5,0,9;
      5,8,0,0,0,0,0,0,0;
      0,0,6,0,1,7,0,0,0;
      7,2,1,4,5,0,0,0,0;
      0,4,5,0,0,1,0,8,0;
      0,3,9,7,0,0,6,0,0;
      0,0,0,8,6,4,9,5,0};

m2 = {0,5,0,0,6,0,0,0,1;
      0,0,4,8,0,0,0,7,0;
      8,0,0,0,0,0,0,5,2;
      2,0,0,0,5,7,0,3,0;
      0,0,0,0,0,0,0,0,0;
      0,3,0,6,9,0,0,0,5;
      7,9,0,0,0,0,0,0,8;
      0,1,0,0,0,6,5,0,0;
      5,0,0,0,3,0,0,6,0};

答え一発。

> sudoku m1;
[
153972468
978546231
462183579
584639127
396217845
721458396
645391782
839725614
217864953
]
2.2545s
> sudoku m2;
[
953762841
624815973
871349652
289457136
165283497
437691285
796524318
318976524
542138769
]
16.7698s

両方の問題を解くように設定したやつを、コンパイルして実行

[sakae@manjaro examples]$ pure -c sudoku.pure
[sakae@manjaro examples]$ time ./a.out
real    0m18.732s
user    0m18.670s
sys     0m0.033s

実行時間は、インタープリタ版と変わらずか。という事は、インタプリタも 実行時に、コンパイルしてるんだな。JITで頑張れってやつ。初回は実行時間が間延び するけど、2回目からは慣れてて、速くなる。

[sakae@manjaro examples]$ ldd a.out
        linux-gate.so.1 (0xb77a1000)
        libpure.so.8 => /usr/local/lib/libpure.so.8 (0xb7420000)
        libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb7337000)
        libm.so.6 => /usr/lib/libm.so.6 (0xb72f1000)
        libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0xb72d5000)
        libc.so.6 => /usr/lib/libc.so.6 (0xb7125000)
        libLLVM-3.3.so => /usr/lib/libLLVM-3.3.so (0xb5a31000)
        libz.so.1 => /usr/lib/libz.so.1 (0xb5a1a000)
        libpthread.so.0 => /usr/lib/libpthread.so.0 (0xb59fe000)
        libffi.so.6 => /usr/lib/libffi.so.6 (0xb59f7000)
        libdl.so.2 => /usr/lib/libdl.so.2 (0xb59f2000)
        libmpfr.so.4 => /usr/lib/libmpfr.so.4 (0xb5996000)
        libgmp.so.10 => /usr/lib/libgmp.so.10 (0xb5923000)
        /lib/ld-linux.so.2 (0xb77a2000)

色々なものがリンクされてるね。

シンボリック

何やら面白そうな、symbolic.pureなんて言うデモ(広告)が置いてあった。能書きを読むと

/* The Pure interpreter can perform symbolic computations with arbitrary term
   rewriting rules. E.g., the following equations implement the arithmetic
   rules of distributivity, associativity and the neutral elements. This isn't
   really enough to get good simplifications, but you get the idea. */

何やら代数っぽいぞ。続いて見てくと

(x+y)*z = x*z+y*z;      x*(y+z) = x*y+x*z;
x+(y+z) = (x+y)+z;      x*(y*z) = (x*y)*z;

x*0     = 0;            0*x     = 0;
x*1     = x;            1*x     = x;
x+0     = x;            0+x     = x;

ふむふむ、結合則とか、交換則とかだな。 例が出てた。

> square x = x*x;
> square (a+b);
warning: rule never reduced: square x = x*x;
a*a+a*b+b*a+b*b
> square (a+1);
a*a+a+a+1

うん、昔やった数学そのものだ。

diff x (u+v)    = diff x u + diff x v;
diff x (u*v)    = u*diff x v + v*diff x u;
diff x x        = 1;
diff x y        = 0 otherwise;

今度は、diffって事は、ひょっとして微分かな?

> diff x (5*x*x+2*x+10);
5*x+x*5+2
> diff x (5*square (x+y));
5*x+x*5+y*5+5*y

うろ覚えの微分の方法を思い出すに、答えは合ってるな。(と、偉そうに言ってみる) 続いて見てくと、

~~a             = a;

敵の敵は味方ですってののpure語的表現だな。にょろ文字が否定を表すと読めばいいんだな。

~(a || b)       = ~a && ~b;
~(a && b)       = ~a || ~b;

そして、今度は、ド・モルガンの定理が出てきましたよ。NORゲートは負論理入力のANDゲート と等価。NANDゲートは負論理入力のORゲートってやつだ。SN7400だけで、随分といろいろな 回路を組んだな。懐かしい。

この後、代数と同じように 分配則と結合則が定義されてて、それを踏まえて、下記の変換例が出てた。

> a || ~(b || (c && ~d));
a||~b&&~c||~b&&d

合ってますかね? おいらには、もう証明不可能ですだ。いやー、ボケたものだな。 論理式で示されるより、回路図を描いて、それを変形すればいいのか。頭の中での 書き換えはきつくなってる。歳は取りたくないものぞ。

units.pure

unitsって単位の事だよな。単位と言えば思いだされる事がある。かの昔、英会話の先生が、 それぞれ自己紹介してくださいって言われた。当たり障りない自己紹介をしたら、いきなり 先生から質問が飛んできた。

なんか、たたみ・マットって言ってるけど、何やねん? コンテキストを考えたら、お前は 何畳の部屋に住んでるねん? って事だな。

外人は、畳を単位にするんだと思い知らされましたよ。昔から言うよね、座って半畳み、寝て 一畳って。昔の人は合理的。

合理的でないのが、アメリカの単位、初めてアメリカへ上陸してTVを付けたらウェザー・レポートを やってた、80度ってどんな所や、まじびびったぞ。

TPPで参入障壁がどうのこうのって言ってるけど、アメリカの独特な単位が障壁に なってるぞ。最高時速55マイルって、どのくらいや? 1ガロンってどのくらいや?

単位変換器を持参しないとだめだな。

> 80*fahrenheit as celsius;
26.6666666666667*celsius
> ans as kelvin ;
299.816666666667*kelvin
> 55*miles as kilometers;
88.51392*kilometers

先月、銀座を闊歩してたら、 4℃なんて店が出てた。デファニーに向かい合ってたぞ。 さぞやクリスマスを控えて混んでいるんでしょうな。かの店、ヨンドシーって読むらしい。 舶来風に、フォー・デグリー・セルシウスって読ませれば、かっこいいのに。名前の由来は、 綺麗な湖底の水温は4℃だからだそうです。

もしこの店が、アメリカに進出するなら、店名を

> 4*celsius as fahrenheit ;
39.2*fahrenheit

39°F に、変更しなければ、意味を成しませんよ。units.pureが有って良かったね。

余り広告を試してばかりなのでは、顰蹙を買いそうなので、前回やったdBm mWの変換器を 定義してみる。

  mW = 0.001*W;
  x*dBm = (10^(x/10.0))*mW;
  x*mW = (10*log x)*dBm;

この式を定義する前に、mW、W,dBmは単位記号だよーって、宣言しとく必要が有ります。

> 1*W as mW;
1000.0*mW
> 20*dBm as W;
0.1*W
> ans as dBm;
207.360313341005*dBm

ありゃ? 最後の変換が間違ってるな。なんでかな?

そんな事で悩むなよ。最近は、RaspberryPiを買うとMathematicaがついてくる そうだから、 そちらに任せればOK。無駄使いしたくない人は、マキシマさんにお願いすればおk。 マキシマさんは、多分SBCLさんとかと吊るんで来てくれるよ。