REPLって便利なんだけど、Emacsのscratchバッファになれているせいか、どうも使いづらい。scratshバッファは平面だけど、REPLは一次元ということがあるのだろう。
ダミーのlispファイルをひらいてそのバッファでコードを書いて——実際の動作はREPLで確認、ということをやっていた。
slimeの関数を眺めていたらslime-scratchというのを見つけた。
おっ。
2018年7月28日土曜日
slime-scratch
2018年7月26日木曜日
スターン数列とフィボナッチ数
スターン数列は,f(0)=0、f(1)=1と二つの再帰的関係f(2n)=f(n)およびf(2n+1)=f(n)+f(n+1)で定義される.これを計算すると,数列は0,1,1,2,1,3,2,1,4,3,5,2,5,3,4と続く.
マーク・チャンバーランド「ひとけたの数に魅せられて」 P11
何をいっているのか、わからない。
スターン数列でググってみたけれど、ヒットしたサイトは本の説明をくりかえしているだけで何のとっかかりにもならなかった。日本語のwikiも見当たらない。
まぁ、f(0)とf(1)はわかる。でもf(2n)=f(n)って何だ? イコールじゃないじゃないか。
しばし、じっと見つめる。
あれ?
もしかしたらf(2n)って偶数のことじゃないか?
すると、f(2n+1)は奇数か。
じゃ、コードはこうか?
(require 'cl) (defun wrk(n) (cond ((= n 0) 0) ((= n 1) 1) ((evenp n)(wrk (/ n 2))) ((oddp n)(+ (wrk (/ n 2))(wrk (+ (/ n 2) 1)))))) (mapcar 'wrk '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) (0 1 1 2 1 3 2 3 1 4 3 5 2 5 3 4)
おっ、正しいみたいだぞ1。
プログラミングができてよかった。
エレン、マフラーを巻いてくれてありがとうって気分だ。
そういえば、フィボナッチ数のコードはよく見かけるけど2、実際に自分で組んでみたことはなかった。たしか、こんな感じ?
(defun wrk(n) (cond ((= n 0) 0) ((= n 1) 1) (t (+ (wrk (- n 1))(wrk (- n 2)))))) (mapcar 'wrk '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) (0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610)
0から15ぐらいならすぐだけど、0から100までにしたら延々、もどってこない……。
2018年7月24日火曜日
竹内郁雄「初めての人のためのLISP」
もっと早くに読んでおくべきだった。
交互リストによるplistのことも書かれているし、keywordについても——CommonLispをはじめていろいろ疑問に思ったことの答えが書かれていた。
もっともずっと以前に読んでいたらこれらのことは気づかずにふーんとスルーしていたにちがいないけど。
2018年7月22日日曜日
setf
setfではassocを使えないんだね1。
CL-USER> wrk
((1 2) (3 4) (5 6))
CL-USER> (setf (assoc 3 wrk) 123)
; Evaluation aborted on #<UNDEFINED-FUNCTION (SETF ASSOC) {1003D452D3}>.
CL-USER>
でもこうすると、中身を変更できるんだ。
CL-USER> wrk ((1 2) (3 4) (5 6)) CL-USER> (setf (cadr (assoc 3 wrk)) 99) 99 CL-USER> wrk ((1 2) (3 99) (5 6)) CL-USER>
これまた、RDBっぽい。
ちなみにまちがって下記のように書いたらSBCLは即エラーになったけれど、ABCLは永久ループにはまりこんでしまった。
CL-USER> wrk ((1 2) (3 4) (5 6)) CL-USER> (setf (cadr (assoc 3 wrk)) wrk)
elispはどうだろう。
(setf wrk '((1 2)(3 4)(5 6))) ((1 2) (3 4) (5 6)) (setf (cadr (assoc 3 wrk)) wrk) ((1 2) (3 #0) (5 6)) wrk ((1 2) (3 #0) (5 6))
なんじゃ?
Footnotes:
setfできるassocがalexandriaにあるらしい。
2018年7月20日金曜日
delete
listを操作する関数で破壊的なものはあまり使う気になれなかった。意外なところに影響がでたりするからだ。でもsetfは便利だな、と思っていて、そういえば、listの一部だけ削除するにはどうしたらいいんだろう。
deleteを見つけて色々、試してみて思った。
これってassocと組み合わせるとすごくね?
CL-USER> wrk ((1 2) (3 4) (5 6)) CL-USER> (delete (assoc 3 wrk) wrk) ((1 2) (5 6)) CL-USER> wrk ((1 2) (5 6)) CL-USER>
RDBみたいじゃん。
deleteのような破壊的関数ではなく、removeというのもあった。
CL-USER> wrk ((1 2) (3 4) (5 6)) CL-USER> (setq wrk2 (remove (nth 1 wrk) wrk)) ((1 2) (5 6)) CL-USER> wrk ((1 2) (3 4) (5 6)) CL-USER> wrk2 ((1 2) (5 6)) CL-USER>
もしかしたら、と思って試してみたら。
wrkとwrk2の要素は共有されていた。
CL-USER> (setf (caar wrk2) 123) 123 CL-USER> wrk ((123 2) (3 4) (5 6)) CL-USER> wrk2 ((123 2) (5 6))
おもしろい。
elispでも同じことができるけど、deleteの比較はequalだった。eqのものはdelq。CommonLispはどうやらeqらしい。:testで変更できるけど。
2018年7月17日火曜日
slime-eval
CommonLispを使うようになってelispをプログラミングする気力がなくなってしまった。
別にLisp四天王でelispが一番、最弱だから、というわけではなく、CommonLisp——SBCL——のレスポンスが速くてelispを使う気がしなくなってしまったのがひとつ。もうひとつは別プロセスで動くので、Emacsがかたまるということがない。こっちの方が理由としては大きいかもしれない。
ただ、やはりエンドユーザとのインターフェイスをもっているというのは大きくてなんだかんだと選択肢はelispということになってしまう。
じゃあバックグランドでCommonLispを動かしてしまえば、いいじゃん。
S式でやりとりできるだろうし。
roswellスクリプトをshell-commandで起動して……とか、考えて面倒くさっ、と思ってしまった。もっと簡単にできないものか。
そもそもEmacsとCommonLispでやりとりしているわけだし。
——slimeに何か、あるんじゃね?
そう思ってつらつらslimeのソースをながめていたら。
「slime-eval」というものを見つけた。
slimeを動かして*scratch*で——。
(slime-eval `(cl-user::+ 1 2 3)) 6
おおっ。
eval自体はSWANK-IO-PACKAGEの中で動いているらしく、packageをフル指定しなければ、ならなかったけれど。
listだって返ってくる。
(setq wrk (slime-eval `(cl-user::list 1 2 3))) (1 2 3) (car wrk) 1
elispとシームレスにやりとりできるじゃないか!
しかも非同期バージョンと思しき、「slime-eval-async」もある。
「asdf::load-system」だってelispから動かせる。
しかし、elispの変数の中身をわたすには——。
(setq wrk 4) 4 (setq wrk (slime-eval `(cl-user::list 1 2 3 ,wrk))) (1 2 3 4)
こうか。
それにしてもこのやり方ってlispにしか、できない芸当だよなぁ1。
Footnotes:
shellもできるか……。
2018年7月15日日曜日
(unintern 'function)
2018年7月13日金曜日
nilってほんとうにNULLなのか!
けっこうdefmethodが好きだ。
というのも引数によって処理を分岐する関数をつくることが多いから。
たとえば(下記はelispだけど)。
(defun y-string-to(x) "stringへ変換" (cond ((null x) "") ((listp x)(y-string-to (car x))) ((symbolp x)(y-string-to (symbol-name x))) ((stringp x) x) ((numberp x) (number-to-string x)) ((vectorp x)(y-string-to (aref x 0)))))
とか。
defmethodを使えば、これらは全部、分けられる。
(defmethod y-string-to((x string))
x)
(defmethod y-string-to((x number))
(format nil "~D" x))
とか。でもstringでもnumberでもないnilのときはどうすれば、いいねん、と思っていたら定義できるのだった。
(defmethod y-string-to((x null))
"")
nilってNULLってタイプなのか!
DEF> (type-of nil) NULL DEF>
2018年7月11日水曜日
Lispに型はないと思っていたら
どうしてか、Lispには型がないとばかり思っていた。
さすがに文字列と数値とかはあるだろうけど、と。
DEF> (type-of :ABC) KEYWORD DEF>
ってやってみて驚いた。
「:」付きってkeywordという型をなのか。
あるじゃないか。型。
以前からよくわからなかった「:」はkeywordという型指定ということになるのか、つまり。もしかしたらリーダーマクロで実装されているんだろうか?
というか、根本的に勘違いしているじゃないか。
Lispはプログラムの実行時まで呼び出すべき変数のデータ型や関数の実体が決まらない動的束縛(late bindingともいう)の言語である。
竹内郁雄「初めての人のためのLisp [増補改訂版]」
2018年7月9日月曜日
みたび、package
たとえば、次のようなpackageとclassを定義したとする。
(in-package :cl-user) (defpackage abc (:use :cl) (:export :<abc>)) (in-package :abc) (defclass <abc> () ((abc :initarg :abc :reader get-abc :documentation "abc"))) (in-package :cl-user) (defpackage def (:use :cl :abc)) (in-package :def) (defclass <def> (<abc>) ((def :initarg :def :reader get-def :documentation "def")))
で、defの中でinstanceを作成。
DEF> (setq wrk (make-instance '<def> :abc "111" :def "222"))
まぁ、WARNINGは多少でるけれど、中身は設定されている。
DEF> (get-def wrk) "222" DEF>
get-abcが効かないのは関数をexportしていないのでDEFから見えていないからだろう。
DEF> (get-abc wrk)
; in: GET-ABC WRK
; (DEF::GET-ABC DEF::WRK)
;
; caught STYLE-WARNING:
; undefined function: GET-ABC
;
; caught WARNING:
; undefined variable: WRK
;
; compilation unit finished
; Undefined function:
; GET-ABC
; Undefined variable:
; WRK
; caught 1 WARNING condition
; caught 1 STYLE-WARNING condition
; Evaluation aborted on #<UNDEFINED-FUNCTION GET-ABC {1003F36EE3}>.
DEF>
ところがslot-valueでabcの中身を見にいってもエラーになる。
DEF> (slot-value wrk 'def)
"222"
DEF> (slot-value wrk 'abc)
; Evaluation aborted on #<SIMPLE-ERROR "~@<When attempting to ~A, the slot ~S is missing from the ~
object ~S.~@:>" {10040EC8D3}>.
DEF>
これがしばらく理解できなかった。
というのもelispのEIEIOではうまくいくからだ。
HyperSpecのdefpackageの説明を見ていてexportしているのはsymbol-nameだと気づいた。functionじゃないんだ……。つまりClassの中で定義しているslot-nameもexportしなければ、いけない。
それならこれでも動くはず。
DEF> (slot-value wrk 'abc::abc) "111" DEF>
実際にexportしてやっても動いた。
(defpackage abc
(:use :cl)
(:export :<abc>
:abc))
DEF> (slot-value wrk 'abc) "111" DEF>
EIEIOで動いていていたのはpackageがないからか……。
でもslot-nameなんてばりばり競合してしまうでaccessorをexportするのが、吉なんだろうな……。accessorならmethodなので同じ名前が存在しても競合する可能性はきわめて低いから。
2018年6月29日金曜日
なんとEmacsが使える!
2018年6月27日水曜日
とりあえず、データ変換
とりあえず、データを加工してごっそり変換するプログラムをつくった。
DBに格納しておいてそれをあとで利用しようというわけ。
ところが変換中、元々残りわずかだったMacBookAirのディスクスペースが満杯になってしまった。あわてて中断したけれど、いかんじゃないか、これは。
まいった。
あれだな、これはPostgreSQLを別のパソコンに持っていくのが吉か。
そう思って全然、使っていないBeelink S1のUbuntuにPostgreSQLをインストールした。psqlも。基本になるデータをpg_dumpでつっこんでリモートアクセスできるように設定して。
動かした。
MacBookAirのCommonLispから。20年分近いデータだ。時間がかかるだろうなぁ、と思っていたらBeelink S1が固まった——固まったと思った。terminalが反応しなくなったのだ。
これだから安物は。
熱暴走か——Beelink S1は熱が溜りやすいとか、なんとかいう噂を耳にしていたのだ。たしかに筐体は熱くなっていた。けれど、何度か、試してみているうちに、原因はBeelink S1ではないことに気づいた。
topコマンドでみると、「kswapd0」というプロセスで固まっている——ように見える。「kswapd0」とはなんぞ。ググってみてページングプロセスらしいことがわかる。そうやってtopコマンドの出力を見直してみると、たしかにswapメモリが0になっていた。Linuxのせいかよ!
いやこんなに簡単に固まるようじゃ、世間様が使うわけはない1。
固まっているように見えるが、実は非常にレスポンスが悪くなっているだけで——何もできないけど。もしかしたら自分のせいじゃね?
組んだプログラムが悪いんじゃね?
でも。
毎回、commitするようにしても固まるし。
毎回、connectしなおすようにしたらsocketを取得できなくなってCommonLisp側がハングアップしてしまうし。
あかんじゃないか。
そりゃあ、プログラムを動かさなければ、固まらないさ。それじゃ、意味がない。swapを拡張しても1年分ほどで固まっているから問題の解決にはならんだろうし。
たぶん状況から考えてリモート接続していると、PostgreSQLがメモリを食い潰しているのだろう(ローカルで動かしたpg_dumpはうまくいっているのだから)。そこでUbuntuにCommonLispの環境をつくってローカル接続からデータ変換するようにしたところ。
うまくいった。
swapメモリはまったく消費せずに2。
2018年6月23日土曜日
HyperSpecをローカルで
hyperspecのデータをどこぞからもってきてローカルの保存して「common-lisp-hyperspec-root」に場所を定義すれば、よいらしい。
common-lisp-hyperspec-root is a variable defined in ‘hyperspec.el’. Its value is "http://www.lispworks.com/reference/HyperSpec/" Documentation: The root of the Common Lisp HyperSpec URL. If you copy the HyperSpec to your local system, set this variable to something like "file://usr/local/doc/HyperSpec/".
となっていたのだけれど、ewwだと「file://usr/local/doc/HyperSpec/」にしていると、うまくいかなかった。ftpで接続へ行ってしまう。
なので色々、試した結果。
"file:/usr/local/doc/HyperSpec/"
「//」を「/」にした。
2018年6月21日木曜日
packageふたたび
ソースを見ると、cl-dbiはdbiというpackageだった。
(defpackage dbi (:use :cl :dbi.error) (:nicknames :cl-dbi) (:import-from :dbi.driver :list-all-drivers
CL-USER> (cl-dbi::select "123")
; in: DBI::SELECT "123"
; (DBI::SELECT "123")
;
; caught STYLE-WARNING:
; undefined function: DBI::SELECT
;
; compilation unit finished
; Undefined function:
; DBI::SELECT
; caught 1 STYLE-WARNING condition
; Evaluation aborted on #<UNDEFINED-FUNCTION SELECT {10065F6893}>.
それに気づかずに、自分もdbiというpackageにしたら。
CL-USER> (defpackage dbi (:use :cl :cl-dbi) (:nicknames :keiba-dbi)) (in-package :keiba-dbi) (defun select(sql) sql) WARNING: DBI also uses the following packages: (DBI.ERROR) 【割愛】 The ANSI Standard, Macro DEFPACKAGE The SBCL Manual, Variable *ON-PACKAGE-VARIANCE* SELECT
cl-cbiにselectが定義されてしまった。
DBI> (cl-dbi::select "123") "123" DBI>
うわっ。
これってcl-dbiを拡張してしまったってこと?1
関数とか、conflictしたらどうするのよ、CommonLispって変、と一瞬、思ったのだけど2、よくよく考えたらC言語とかでも関数名がかぶったら片方が有効になるだけだった。
そう思うと、javaの、あのディレクトリ構成にマップされた命名規則はconflictを避けるためのものなんだなぁ、といまさらながら。3。
2018年6月19日火曜日
ふぉー、まっと
sql文を組み立てるため、formatを使った。
CommonLispのformatは全然、ちがうというのは知っていたのだけれど、まぁ、「~S」が文字列だろう、と簡単に考えたら全然、ちがっていた。
CL-USER> (format t "あああ~Sかかか" "ABC") あああ"ABC"かかか NIL
まさか「"」付きになるとは。
CL-USER> (format t "あああ~Aかかか" "ABC") あああABCかかか NIL
「~A」か。
しかも第一引数で出力先を指定している。nilでは標準出力になる。tで変数へ出力できる。まるでprintf()とsprintf()みたいだ。でもformatの構文は全然、ちがう。
2018年6月17日日曜日
あっ、ぷらい
cl-dbiで返ってきたデータにはめんくらってしまったのだけど。
こんなデータ。
(:|date| 3281472000 :|place| "A7")
DBのカラム名と値が交互にならんでいる。
きっと何か、うまくやる方法があるんだとは思っていた。値をひっぱりだすのにgetfを使うというのは了解した。
CL-USER> (setq wrk '(:|date| 3281472000 :|place| "A7")) (:|date| 3281472000 :|place| "A7") CL-USER> wrk (:|date| 3281472000 :|place| "A7") CL-USER> (getf wrk :|date|) 3281472000
きっとkeyとしても使えるにちがいない。それがよくわからなかった。applyかな、とは思っていたのだけど、うまく動かない。
CL-USER> (defclass <wrk> ()
((date :initarg :date)
(place :initarg :place)))
#<STANDARD-CLASS COMMON-LISP-USER::<WRK>>
CL-USER> (apply #'make-instance '<wrk> wrk)
; Evaluation aborted on #<SB-PCL::INITARG-ERROR {10048A9723}>.
CL-USER>
ふと気づく。
CL-USER> (defclass <wrk> ()
((date :initarg :|date|)
(place :initarg :|place|)))
#<STANDARD-CLASS COMMON-LISP-USER::<WRK>>
CL-USER> (apply #'make-instance '<wrk> wrk)
#<<WRK> {1004641BE3}>
CL-USER>
そっか。
defclassの定義で「:date」としていたのが、まちがいだったのだ。
CommonLispのsymbol名は基本、大文字小文字の区別がないのだけれど、「|」付の場合は区別しているという意味だった(それは一応は知っていた)。「|」付で定義すると、区別あり、と定義できるのだった(これには気づいてなかった)。DBのカラム名は普通、大文字小文字の区別があるのでこうなっているのだろう。
これでcl-dbiからの返却値を一気にクラスに流しこむことができる。
この「apply」を使うやり方に気づいてから一気にコード量が減った。ちまちまと値を設定しているところがごっそりなくなったので。
applyって凄い1。
それにしても「:」付のsymbolってどうやってつくるんだろう?
単純にinternしてもだめなんだよなぁ。
CL-USER> (intern "date") |date| :INTERNAL CL-USER>
Footnotes:
いや、listが凄いのかも。
2018年6月16日土曜日
defclass
データのかたまりはやはりstructか、classにした方がいいよな。
EIEIOを使ったのときはクラスを定義した。
あれが丸々、使えるんじゃね?
そう思ってコピったところ、エラーになる。しかもdefclassのところ。何でよ。同じじゃないの。目を皿にしてようやく気づいた。EIEIOでは「:docment」だけど、CLOSでは「:documentation」だ……。
2018年6月15日金曜日
cl-project
「~/.roswell/」の下をつらつら見ていると、「local-projects」というフォルダがあった。二カ所。
~/.roswell/local-projects/
~/.roswell/lisp/quicklisp/local-projects/
ああ、ここの下に自分がつくりたいプログラムをいれておけば、いいんだな、と見当をつけてとりあえず、「cl-project」でprojectを作成した。
CL-USER> (cl-project:make-project #p"~/.roswell/local-projects/keiba/" :author 名前 :email メールアドレス :license ライセンス :depends-on '(:cl-dbi :cl-ppcre)) writing ~/.roswell/local-projects/keiba/keiba.asd writing ~/.roswell/local-projects/keiba/keiba-test.asd writing ~/.roswell/local-projects/keiba/README.org writing ~/.roswell/local-projects/keiba/README.markdown writing ~/.roswell/local-projects/keiba/.gitignore writing ~/.roswell/local-projects/keiba/src/keiba.lisp writing ~/.roswell/local-projects/keiba/t/keiba.lisp T
(asdf:load-system :keiba)でsystemをロードすると、cl-dbiもロードされる模様。ふむ。なるほど。
CL-USER> (asdf:load-system :keiba) ; compiling file "/Users/yamada/.roswell/local-projects/keiba/src/keiba.lisp" (written 10 JUN 2018 09:29:31 AM): ; compiling (IN-PACKAGE :CL-USER) ; compiling (DEFPACKAGE KEIBA ...) ; compiling (IN-PACKAGE :KEIBA) ; /Users/yamada/.cache/common-lisp/sbcl-1.3.11-macosx-x64/Users/yamada/.roswell/local-projects/keiba/src/keiba-TMP.fasl written ; compilation finished in 0:00:00.005 T
(ros::load-system "keiba")でも同じみたいだ。
わからないのは
CL-USER> asdf:*central-registry* (#P"/Users/yamada/.roswell/lisp/quicklisp/quicklisp/")
なのに、#p"~/.roswell/local-projects/keiba/"をasdfが認識していること。qlが下記のようになっているのでRoswellはわからなくもないけど。
CL-USER> ql:*local-project-directories* (#P"/Users/yamada/.roswell/local-projects/" #P"/Users/yamada/.roswell/lisp/quicklisp/local-projects/")
不思議。
2018年6月13日水曜日
環境
当たり前の話だけれど、REPLを立ち上げなおしたら1今までの作業がきれいさっぱり消えてしまった。あらためてちまちまとloadしなおさなければ、いけないとは思えず、たとえば、Emacsでいえば、load-pathみたいな仕組みがどこかにあるんじゃなかろうか?
どうやらASDFとか、quicklispとかがその仕組みらしいのだけれど。
require, ASDF, quicklispを正しく使う
上記以外にもあちらこちらのサイトを読んで考えこんでしまった。なんか、情報が錯綜している。頭の中がごちゃごちゃになってしまった。defsystemってなんぞ2。そこで「SBCL」とか、「asdf」のinfoを読みなおしながら自分の環境のパス構成を確認。なんか、おかしい。
「~/.sbclrc」とか、「~/common-lisp/」とか、見当たらない。
それを作成して設定すれば、いいのか? でもなんか、根本で道を誤っているような気がする。
あっ。
思い出した。
下記のサイトを見てRoswellを使ってsetupしたんだった。
Common Lispとリアル・ワールドを繋ぐ「Roswell」の紹介
ということはそもそもRoswellを調べなきゃ、いかんのと、ちゃう3?
2018年6月11日月曜日
plistなんて知らんがな
それにしてもcl-dbiで返ってきたデータにはめんくらった。
こんな感じのlistになっていたからだ。
(:|date| 3281472000 :|place| "A7" :|count| 3 :|day| 7 :|raceno| 1 :|umano| 1 :|name| "アドマイヤマドンナ" :|sex| "牝" :|age| 2 :|time| 1137/10 :|umaorder| 13 :|odds| 502/5 :|ninki| 13 :|stable| "南井 克巳" :|weight| 54 :|forecast| :NULL)
中身のことは別にどうでもいい1。
「:|date|」の部分はテーブルのカラム名なのだろう、ということはわかるけれど、「:」と「|」には何か、意味があるのか? そもそもこんな形式で? mapcarで舐めてdateの次のものを値とあつかうなんて面倒じゃないか?
たとえば、pg.elだと((data . 3281472000)(place . "A7")….)みたいな感じで返却されてきていた。alistというらしいが、これならassoc2で簡単に値を求めることができる。それがなんでまたこんな形式に?
alistに変換する関数を書くか、と考えながらあちらこちら調べていたらgetf? 何、それ? おいしいの?
返却されてきたlistは実はplistというものらしい。
(getf list :|date|)
で、値を求めることができた。
getfは素のelispにはなく、cl.elに定義されていた。
でも「:」と「|」の謎は解けていないのだった。