2018年6月29日金曜日

なんとEmacsが使える!

 重い処理をSLIMEからCommonLispをやらせていてちょっと感動した。
 なんとEmacsが使えるのだ1
 elispにやらせていたときはなーんもできんようになっていたのに。処理自体も全然、速いし。これからはelispではなく、CommonLispを使うようになるかも。

Footnotes:

1

よくよく考えたら当たり前

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

Footnotes:

1

一瞬、FreeBSDをインストールしなおそうか、とは思ったけど。

2

腹立たしいことにそのあと、MacBookAirに変換したデータをつっこんでもディスクが満杯になるようなことはなかった。

2018年6月25日月曜日

カッコがあるからいいんじゃないか

 Lispでプログラミングしていると、粘土をこねているような感覚がある。
 ぐにゅぐにゅと、つくったものをくっつけたり、ちぎったりしているような感覚。ほかの言語ではそんなことはないのでおそらく、Lisp系の言語に固有な感じなのだろう——不思議なことにElispよりCommonLispの方がその感覚が強い。CommonLisp特有の機能を使っているわけじゃないのに。気持ちの問題かな?
 ほかのプログラム言語はもうすこし不自由な感じで——こねられない(感覚としていは切り貼りしているような感じ。粘土より硬い)——、何でだろう、とずっと疑問だった。
 もしかしたらそれはS式のおかげではないか?
 Lispが嫌われる大きな理由のひとつ。あのカッコだらけの姿。
 はじめてLispのプログラムを見たときはのけぞったあのカッコ。こんな言語、わけがわからん、と思ったのだけれど。

 これはぼくだけのことなのかもしれないけど、プログラムを組むとき、トップダウンに上から単純に組んでいくわけじゃない。かといってボトムアップともかぎらず、いきなり真ん中ということも多い。
 これは形が見えているところから組むからだろう。——重要なのは組んでみると、まちがいに気づくことが多々あるということだ。
 この形はよろしくない。
 で、プログラムをこねる。
 よくやるのがロジックを外出しすることだ。同じロジックを組みはじめると、イラッとする。それで共通関数にくくりだす。
 これがLispだと楽にできる。
 だってすでにカッコでくくられているから。Ctrl-Meta-kでkillしてyankするだけ。ほかの言語だと、行レベルでの範囲選択になるので範囲指定に頭を使うし、ちょっとほかの部分とまじっていたりしてひと手間かかる。もちろんLispの場合でもロジックがまじってしまっていることはあるけど、概ねだいじょうぶ。たぶんロジックがカッコでくくられているからだろう。まじっていない部分のみを取り出すのは容易だ。
 逆に、これは細かくしすぎた、と思ったときはカッコごと、ほかのカッコの中に移動させて、まぜる。

 こうやって考えると、LispがS式であることは重要。

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

Footnotes:

1

プロンプトを見ると、何が起きたか、一目瞭然だった。

2

なんとなく、Lispってレキシカルって思った。

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:

1

いや、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を正しく使う

第6回 Common Lispライブラリを書く

 上記以外にもあちらこちらのサイトを読んで考えこんでしまった。なんか、情報が錯綜している。頭の中がごちゃごちゃになってしまった。defsystemってなんぞ2。そこで「SBCL」とか、「asdf」のinfoを読みなおしながら自分の環境のパス構成を確認。なんか、おかしい。
 「~/.sbclrc」とか、「~/common-lisp/」とか、見当たらない。
 それを作成して設定すれば、いいのか? でもなんか、根本で道を誤っているような気がする。
 あっ。
 思い出した。
 下記のサイトを見てRoswellを使ってsetupしたんだった。

Common Lispとリアル・ワールドを繋ぐ「Roswell」の紹介

 ということはそもそもRoswellを調べなきゃ、いかんのと、ちゃう3

Footnotes:

1

(slime-restart-inferior-lisp)

2

asdfが提供している何かですかね

3

manがあった。

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に定義されていた。

 でも「:」と「|」の謎は解けていないのだった。

すこしわかった

Footnotes:

1

競馬のデータだ。

2

(nth 1 (assoc 'date list))みたいな感じで。

2018年6月9日土曜日

packageってなんぞ

 とりあえず、昔、インストールしておいたSLIMEでREPLは立ち上がる。

 まず、CommonLispからDBアクセスはどうしたらよいのか。
 対象はPostgreSQL。ググったら「CommonLispとRaspberryPiでデータベース~その1~」がひっかかった。つらつら。なるほど「cl-dbi1というものがあるらしい。
 さっそく例題にしたがってやってみる。
 あっさりとデータが返ってきた2
 へえ。
 コードをながめる。

;; Quicklispで「cl-dbi」をロード
(ql:quickload :cl-dbi)

;; パッケージ定義
(defpackage psql-package
(:use :cl

:cl-dbi))

;; パッケージに入る
(in-package :psql-package)

;; データベースへ接続
(defvar connection
(dbi:connect :postgres

:database-name "test"

:username "nobody"

:password "1234"))

;; クエリを実行
(let* ((query (dbi:prepare connection "SELECT * FROM hello"))
(result (dbi:execute query)))
(loop for row = (dbi:fetch result)
while row
do (format t "~A~%" row)))

 ——packageってなんぞ?
 いや、そういうものがあるらしいということは知っていたけど、腑に落ちてはいなかった。「in-package」があるってことは「out-package」もあるのか?3
 elispにはそんなものないし。
 ここでようやく気づく。
 思ったより敷居が高いぞ、CommonLisp。
 オライリー本が必要だ。でもM.D. ConradBarsk「Land of Lisp」ぐらいしか、ないよなぁ。そういえば、ポール・グレアムの「ANSI CommonLisp」をもっていたっけ。package。package。ふむふむ。よくわからん。でもなんとなく、わかった。
 やってみたところ、packageの中にpackageをつくるようなことはできないようだった4。平面にぼこぼこと溜池があるようなイメージかな。「in-package」というより「change-package」?

 いずれにしても1ファイル1パッケージにした方がよさそうだ。

Footnotes:

1

作者は日本人でLispハッカーとして有名な人だった。

2

その直後、pg.elが動かなくなっていることに気づいてあひょひょひょ〜、となった。

3

なかった。

4
CL-USER> (defpackage abc
       (:use :cl))
#<PACKAGE "ABC">

CL-USER> (in-package :abc)
#<PACKAGE "ABC">

ABC> (defpackage def
  (:use :cl))
#<PACKAGE "DEF">

ABC> (in-package :def)
#<PACKAGE "DEF">

DEF> (defun wrk() 999)
WRK

DEF> (in-package :cl-user)
#<PACKAGE "COMMON-LISP-USER">

CL-USER> (def::wrk)
999

CL-USER> (abc::def::wrk)
; Evaluation aborted on #<SB-INT:SIMPLE-READER-ERROR "too many colons in ~S" {1005C4D243}>.
CL-USER>

2018年6月7日木曜日

CommonLispはじめました

 というわけで、何年も前にインストールしていたCommonLispを使いはじめた。EmacsのSLIMEからちょこちょこいじってみたのだけど、ショックなことにslime-autodoc-modeがオンになっていると、空白キーがコンフリクトしてしまってSKKで漢字変換ができない。ひらがなは入力できるけど。
 local-set-keyで両方を呼ぶような関数をでっちあげて空白キーに割り当てただけど、どういうわけか、いつのまにか、slime-autodoc-spaceに上書きされてしまう。
 うーむ。

2018年6月5日火曜日

pg.elが動かなくなってしまった……

 まったくの出来心からMacBook AirのEmacsをバージョンアップしてしまった。Emacs24からEmacs25へ。いつものなら動かなくなってしまうのが嫌で極力、バージョンアップはしないようにしているのだが。パソコンを買い替えるタイミングで最新にするようにしていた。
 ついでに、と思ってHomwbrewをupgradeしたのが、そもそもまちがい。
 EmacsもPostgreSQLも何もかも動かなくなってしまった。Homebrewのパッケージのぼろぼろになってしまい、リカバリーするのに何日もかかってしまう——いっそ、TimeMachineで元に戻してしまおうか、と思ったくらい。
 ようやく元にもどった、と安心したのだが、EmacsからPostgreSQLへアクセスできなくなっていることが判明。pg.elからPostgreSQL10へのアクセスがプロトコルの問題でできなっていた。pg.elのバージョンアップは止まったまま……。
 はまった。
 PostgreSQLのデグレーションにも失敗し。
 かっとなって。
 psqlを起動してPostgreSQLのデータを取得する関数をつくる。

2018年6月2日土曜日

VNC for Ubuntu

 Ubuntuの画面共有にMacから接続できない。
 ディスプレイってけっこう邪魔だからVNC接続できるようにしておきたいんだが。——MacのVNCもだめだし、TigerVNCもだめで、RealVNCからもだめ。RaspberryPiへは接続できるんだからVNCサーバを同じものにしてやれば、いいか、と思ってふと思う。そもそも、UbuntuのVNCサーバって何?

dpkg -l

 で、見てみたら「Vino」とかいうやつだった。
 それでググってみたら理由がわかった。デフォルトでは暗号化して通信しているらしい。解決策も判明。

gsettings set org.gnome.Vino require-encryption false

 無事、Macの「サーバへ接続」から接続可能になった。

 ところが、Beelink S1で画面が開いていないと、VNCの画面は真っ暗なのだった……。やはりVNCサーバを切り替えるしかないみたいだ、結局。