OpenLisp
明けましておめでとうございます。本年も宜しくお願い致します。
OpenLisp
ひょんなことから、 OpenLisp by Eligis なんてのを知った。 Downloads にあるように、色々なOSで動く。
OpenLispのOpenは、ソースがオープンになってるのではなくて、いろんなプラットフォームに対してのOpenを目指しての事。それにしても、多数のOS環境を用意するのは大変だろうに。
ソースが欲しければ、メールで作者様を説得して下さいとの事。決してクローズでは無いらしい。フリー版には、コンパイラーが付いていないけど、ちょいと使うには十分だろう。 まあ、Comparison of ISLISP implementations. なんてのを見ると、そのうち欲しくなるかも。
開発方針として
OpenLisp is KISS (Keep It Small and Simple) for full conforming implementation of ISO/IEC 13816 ISLISP Language the International Standard version of Lisp.
小さくてシンプルにだ。どこかのCL連中(とリナ軍団)に、捧げたい言葉だな。
ドキュメントが公開されてる。何を血迷ったか、doc式の奴しか無い。しょうがないので、libreofficeで開いて、htmlでセーブしたよ。PDFぐらいはサポートしてるかと思ったけど、ちと甘かった。
とか思っていたら、アイコンをぷちゅっとするだけで、PDFに輸出出来る事に気が付いた。普段は、メニューにあるアイコンなんて見ないからね。
2020/12/28 07:15 1,150,432 olus.html 2020/12/28 07:17 636,877 olus.pdf 2020/12/28 07:17 266,906 olus.txt
run OpenLisp
取り合えずリナ用を落として実行。
sakae@pen:/tmp/openlisp-11.0.0$ ./uxlisp ./uxlisp: /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found (required by ./uxlisp)
Debianでは、どうやら根幹のバージョンが古いようで、起動せず。Changelogを見たら、Fedora系しかサポートしていない。営業戦略だな。しょうがないので、OpenBSD(64Bit)で試す。
2020/10/22: Qualified port on OpenBSD 6.8 (x86_64 with Clang 10.0.1). 2020/06/05: Qualified port on OpenBSD 6.7 (x86_64 with gcc 4.2.1/Clang 8.0.1). 2020/02/03: Qualified port on OpenBSD 6.6 (sparc64 with gcc 4.2.1). 2019/10/28: Qualified port on OpenBSD 6.6 (x86_64 with gcc 4.2.1/Clang 8.0.1). 2019/07/03: Qualified port on OpenBSD 6.5 (x86_64 with gcc 4.2.1/Clang 7.0.1). 2018/10/21: Qualified port on OpenBSD 6.4 (x86_64 with gcc 4.9.3/Clang 6.0.0). 2017/03/05: Qualified port on OpenBSD 6.0 (i386 & x86_64 with gcc 4.9.3). 2016/09/03: Qualified port on OpenBSD 6.0 (i386 & x86_64 with gcc 4.9.1). 2016/09/01: Qualified port on OpenBSD 6.0 (i386 with gcc 4.2.1). 2015/10/24: Qualified port on OpenBSD 5.8 (x86_64 + PGO) with egcc 4.9.3. 2015/10/04: Qualified port on OpenBSD 5.7 (x86_64 + PGO) with gcc 4.2.1. 2011/11/02: Qualified port on OpenBSD 5.0 (i386/x86_64) with gcc 4.2.1. 2011/01/05: Qualified port on OpenBSD 4.8 (i386/x86_64) with gcc 4.2.1. 2003/01/02: Qualified port on OpenBSD 3.1 (Intel).
これ、ChangelogからのOpenBSDサポート状況。どちらも実績が長いですなあ。
ob$ ./uxlisp ;; OpenLisp v11.0.0 (Build: 6650) by C. Jullien [Oct 29 2020 - 23:54:42] ;; Copyright (c) Eligis - 1988-2020. ;; System 'unix' (64bit, 4 CPU) on 'ob.localhost.jp', ASCII. ;; Reading startup .. ;; God thank you, OpenLisp is back again!
今度は、ちゃんと起動して素敵なバナーが出て来た。READMEにベンチマークしろって出てたんで、試してみた。
? (load "contrib/gabriel.lsp") 01Fib : ok, time = 0.001s. ( 0 GC) 02Tak : ok, time = 0.005s. ( 0 GC) 03Stak : ok, time = 0.009s. ( 0 GC) 04Ctak : ok, time = 0.080s. ( 0 GC) 05Takl : ok, time = 0.007s. ( 0 GC) 06Takr : ok, time = 0.007s. ( 0 GC) 07Boyer : ok, time = 0.064s. ( 0 GC) 08Browse : ok, time = 0.075s. ( 3 GC) 09Destru : ok, time = 0.011s. ( 0 GC) 10Travini : ok, time = 0.073s. ( 0 GC) 11Travrun : ok, time = 0.336s. ( 0 GC) 12Deriv : ok, time = 0.012s. ( 1 GC) 13Dderiv : ok, time = 0.014s. ( 1 GC) 14Divit : ok, time = 0.010s. ( 0 GC) 15Divrec : ok, time = 0.009s. ( 0 GC) 16FFT : ok, time = 0.058s. ( 0 GC) 17Puzzle : ok, time = 0.076s. ( 0 GC) 18Triang : ok, time = 1.029s. ( 0 GC) 19Fprint : ok, time = 0.001s. ( 0 GC) 20Fread : ok, time = 0.001s. ( 0 GC) 21Tprint : ok, time = 0.000s. ( 0 GC) 22Frpoly : ok, time = 0.178s. ( 0 GC) 2.056 s.
使ってるライブラリーは、極普通なものだけ。
ob$ ldd uxlisp | cut -b 35- Type Open Ref GrpRef Name exe 2 0 0 uxlisp rlib 0 1 0 /usr/lib/libpthread.so.26.1 rlib 0 1 0 /usr/lib/libm.so.10.1 rlib 0 1 0 /usr/lib/libc.so.96.0 ld.so 0 1 0 /usr/libexec/ld.so
setup openlisp
ここまでは、導入テスト。普段使いするなら、ちゃんとした場所に配置して、emaacsと併用する事になる。
openlisp-11.0.0って言う2.1M程のdirを、/usr/local/openlispって名前で配置した。 これに合わせて、.profileに設定を書く。
export OPENLISP=/usr/local/openlisp PATH=$PATH:/usr/local/openlisp
openlisp.elは、.emacs.d/lisp/の下で集中管理する事にしてるんで、本体側からcpしておいた。
(setq inferior-lisp-program "/usr/local/openlisp/openlisp -emacs") (load-file "~/.emacs.d/lisp/openlisp.el")
説明書では、本体側にリンクが必要無いとなってたが、uxlispにリンクしないと動かなかった。 後は、M-x run-lispで起動する。裸のopenlispより格段に使い易くなる。
? (dynamic *system-directory*) ;; elapsed time = 0.0000s, (0 gc). = "/usr/local/openlisp" ? (dynamic *system-path*) ;; elapsed time = 0.0000s, (0 gc). = ("." "/usr/local/openlisp/lib" "/usr/local/openlisp/net" "/usr/local/openlisp/contrib" "/usr/local/openlisp/bench" "/usr/local/openlisp/tst")
本体側の各dirが、検索PATHに追加されてる。
? (room) System name: openlisp, pointer size 8, character size: 1 Type Call Init MemInit Used MemUsed Free MemFree Free% user 1 0 0 0 0 0 0 100% cons 0 252928 4046848 5856 93696 247072 3953152 97% symbol 0 15616 999424 1147 73408 14469 926016 92% string 0 252928 4046848 1298 20768 251630 4026080 99% vector 0 252928 4046848 392 6272 252536 4040576 99% float 0 0 0 0 0 0 0 100% integer 0 0 0 0 0 0 0 100% heap 0 16777216 16777216 58520 58520 16718696 16718696 99% Total 1 0 29917184 0 252664 0 29664520 99% ;; elapsed time = 0.0086s, (1 gc). = t
これがメモリーエリアの使用状況。エリアが細かく分けられている。エリアの特性に合わせて、最適な管理を行う為だろう。
fib
(defun fib (n) (cond ((= n 1) 1) ((= n 2) 1) (t (+ (fib (- n 1)) (fib (- n 2))))))
fibを肴に、ちょっとお遊び
? (fib 40) ;; elapsed time = 17.9737s, (0 gc). = 102334155
OpenBSDは、スピード控え目です。
compile-file
バイナリー版にもコンパイラーが付いていて、VM語に変換される
? (compile-file "fib.lsp") ;; elapsed time = 0.0227s, (0 gc). = t ? (load "fib.lap") ;; elapsed time = 0.0001s, (0 gc). = t ? (fib 40) ;; elapsed time = 8.7163s, (0 gc). = 102334155
裸のコードに比べてスピードが2倍になった。
(set-dynamic 'load *situation*) (openlisp:lap-require "loader") (lap-loader '( ;; ;; fib ;; ((fentry fib 1 0 0) (param 0) (jeq _l004 '1) (jneq _l003 '2) (move a1 '1) (return) _l003 (gsub1 a1) (recurse 1) (move a2 a1) (param 0) (gsub a1 '2) (recurse 1) (gadd a2 a1) _l004 (return) (end)) ))
これの解説は、説明書に出てた。有料版だとこれをC語に変換してC++でコンパイルするらしい。 そうすれば、更に10倍ぐらい速くなるそうな。
trace
traceはlisp系の必需品。
? (load "fib.lsp") ;; elapsed time = 0.0001s, (0 gc). = t ? (trace 'fib) ;; elapsed time = 0.0000s, (0 gc). = fib ? (fib 3) fib -> (n = 3) fib -> (n = 2) fib <- 1 fib -> (n = 1) fib <- 1 fib <- 2 ;; elapsed time = 0.0001s, (0 gc). = 2
debug
書いたコードがBUGを含んでいると、下記のようにエラーになる。そんな時はdebuggerの出番だ。debuggerをイネーブルにしてあげてから、再実行。
? (fib 4) ** <domain-error> : function '+' requires a <number> received "bad". ? (debug t) ;; elapsed time = 0.0000s, (0 gc). = t ? (fib 4) ;; OpenLisp symbolic debugger. ;; Enter '?' for help, ':q' to quit. ** <domain-error> : function '+' requires a <number> received "bad". [001] (fib 3) [002] (fib 4)
履歴出てる。どうやら、(fib 2)の所で数値を期待するも文字列だったので、演算出来なかった模様、なんて事が分かる。debugを止めるには、nilにする。
speed check
(fib 40)が遅すぎると思ったんで、他と比べてみる。
たまたま入っていたeclです。オリジナルは随分昔の物だけど、今現在もちゃんとメンテナンスされていました。
ob$ ecl ECL (Embeddable Common-Lisp) 20.4.24 (git:UNKNOWN) Copyright (C) 1984 Taiichi Yuasa and Masami Hagiya : Copyright (C) 2020 Daniel Kochmanski and Marius Gerbershagen ;;; Loading "/tmp/fib.lis" #P"/tmp/fib.lis" > (time (fib 32)) real time : 3.928 secs run time : 3.700 secs gc count : 67 times consed : 139411808 bytes 2178309 ;;; Loading "/tmp/fib.fas" #P"/tmp/fib.fas" > (time (fib 32)) real time : 1.136 secs run time : 1.130 secs gc count : 1 times consed : 64 bytes 2178309 > (time (fib 40)) real time : 53.259 secs run time : 53.270 secs gc count : 1 times consed : 64 bytes 102334155
コンパイルすると、4倍近く増速になる。組み込み用途を目指しているので、ふっとプリントが軽井沢。openlispさすがに速いな。
debian(32Bit)と言うハンディが有る環境から、scheme界のエースが出場します。
debian:tmp$ scheme fib.ss Chez Scheme Version 9.5.3 Copyright 1984-2019 Cisco Systems, Inc. > (time (fib 40)) (time (fib 40)) no collections 2.348954777s elapsed cpu time 2.384591957s elapsed real time 0 bytes allocated 102334155
contrib/gnuplot.lsp
(defun trigonometrie () (with-open-output-pipe (gp "gnuplot -persistent") (format gp "set samples 2000~%") (format gp "plot abs(sin(x))~%") (format gp "rep abs(cos(x))~%")))
(mandelbrot 200) ;; better with 1000 or more. なんて書いてあったので、Openlispで計算したデータをgnuplotに送っているかと思ったら違った。丸投げしてgnuplotに計算させてる。
そんな事より、with-open-output-pipeが、どう実現されてるか興味がある。多分マクロだろう。そしてその成分はpipeをオープンするやつと、unwind-protectと想像した。
lib/startup.lsp
(defmacro with-open-output-pipe (stream &rest body) `(let ((,(car stream) (open-output-pipe ,@(cdr stream)))) (unwind-protect (progn ,@body) (close ,(car stream)))))
ビンゴ!! このパターンは結構使い道があるぞ。ユーザーマニュアルに載ってた例。
(defmacro with-client-socket (socket &rest body) `(let ((,(car socket) (socket))) (when ,(car socket) (unwind-protect (when ,`(connect ,@socket) ,@body) (close ,(car socket)))))) (defun daytime () (with-client-socket (st "127.0.0.1" "daytime" "tcp") (read-line st () ‘eof)))
ローカルホストのdaytimeポートにtcpを使って接続しろ、かな?
debian:doc$ grep daytime /etc/services daytime 13/tcp daytime 13/udp
udpポートもサポートされてるのか。macroを使って、手軽に構文を作れるってとっても素敵。
debug on eisl
> (load "tests/verify.lsp") enter COPY-GC free=6999994 exit COPY-GC free=7000000 All tests are done! T > (debug) Unbound function at eval DEBUG debug mode ?(help) >>? ? help :a abort :b backtrace :d dynamic environment :e environment :i identify examining symbol :q quit :r room :s stepper ON/OFF other S exps eval
明示的にdebug手続きを呼び出せる。
>>:r EP = 0 (environment pointer) DP = 10747982 (dynamic pointer) HP = 6696963 (heap pointer) SP = 0 (stack pointer) FC = 5998368 (free counter) AP = 0 (arglist pointer) LP = 0 (shelter pointer) GC = 1 (GC switch 0=m&sGC 1=copyGC) WP = 10863751 (work area pointer) SW = 1 (current work area 1or2)
そして、gdbを使わなければ見られないような重要なレジスターを一覧出来る。Pが付くレジスターは、HEX表示の方が便利だろうか? 実験した限りでは、heap配列のインデクス番号みたいなんで、整数の方が分かり易いか。 こんな感じね。
> (heapdump 6696880) addr F TAG CAR CDR AUX NAME 6696880 FRE EMP 6696881 FRE LIS 6696930 6696989 0000021 6696882 FRE EMP 6696883 FRE EMP 6696884 FRE LONGN 274 6696885 FRE BIGX 274 6696886 FRE EMP 6696887 FRE BIGX 0 6696888 FRE EMP 6696889 FRE EMP 6696890 FRE EMP T