知らんことだらけ。
そして、理解し切れんことだらけ。
とくにコンディションについてはjavaとかのtry/catchと同じだと思っていたので目が点。あとdefgenericとか、いろいろ。奥が深すぎるぜ、CommonLisp。
著者は「Coders at Work プログラミングの技をめぐる探求」と同じ人だった。ついさっきまで気づかなかったのだけれど、それにしても「Coders」を読んだときに得た「気づき」ってなんだろう。なーんも思い出せん。
知らんことだらけ。
そして、理解し切れんことだらけ。
とくにコンディションについてはjavaとかのtry/catchと同じだと思っていたので目が点。あとdefgenericとか、いろいろ。奥が深すぎるぜ、CommonLisp。
著者は「Coders at Work プログラミングの技をめぐる探求」と同じ人だった。ついさっきまで気づかなかったのだけれど、それにしても「Coders」を読んだときに得た「気づき」ってなんだろう。なーんも思い出せん。
どうもタイピングで指にけっこう、力を入れているらしく、指の背面側の筋が張る。普通にしているときはあまり気づかないのだけど、ぎゅっと握り拳をつくってみると、はっきりとわかる。ちょっと痛い。
昔はこういう疲れ方はしなかったような?
これはあれだな。
MacBook Airのキーが浅いから指先を叩きつけるようにしてしまっているからだな(仮説)。
じゃ、深いキーボードにしてみるか。
候補は以前、使っていたことがあるHHKあたり。小さいし。だた、音がなぁ、タイプ音がけっこう、うるさいんだよな。ほとんどタイプライターのようになってしまう。
それであきらめていたんだけど、HHKのキーボードは20%静音になったという。じゃ、というわけで、使ってみることにした。でも以前はデスクトップだった。
だから今だとこうなってしまう。
ケーブルの挿しこみ口がちょっと計算外だった。
| -No- | 時刻 | 時間 | 帆走距離 | 最高速 | 平均時速 | P% |
|---|---|---|---|---|---|---|
| 1 | 14:36-14:58 | 22分 | 2.3km | 18.63km/h | 6.25km/h | 1.33 |
| 2 | 15:01-15:16 | 14分 | 1.5km | 15.36km/h | 6.44km/h | 0.12 |
| 3 | 15:25-16:09 | 44分 | 2.1km | 17.92km/h | 2.90km/h | 0.30 |
| 4 | 16:41-17:15 | 34分 | 1.9km | 11.43km/h | 3.40km/h | 0.00 |
total 115分 7.9km 4.1km/h
とにかく遠い。
9時前に出立したというのに到着したのは13時半すぎ。
めげる。
風は入りはじめているらしく、ドラゴンビーチ、FUNビーチともにセイルが湖面にある。でもあまり、動いていない。セイルをセッティングしているうちに湖面がブローが入って白波がたつ瞬間があった。
——きたか?
ところが出艇したときには陽が翳り、風はなくなってしまう。
風上の吹き出し口付近だけプレーニングしているのでひたすらそちらへ上っていってみたけれど、風は全然、足りず。
ノープレーニング1。
FUNビーチの上側の岸で休憩したらあまりの風のなさにビーチスタートすらできなくなくて身動きできなくなる。足をだれかがつついている。しかたないので腹をくくってセイルアップする。
FUNビーチの下側へもどる。
だめだ。今日はだめだ。
4時ぐらいに風がふたたび、入りはじめた感じがあって出艇して沖ではまる。完全に風がなくなり、まわりにだれもいない状況。セイルアップもできず、道具を引いて泳ぐことしばし。
ブロー。
あわててボードによじのぼり、セイルアップ2。
岸を目指す。
FUNビーチからプロ選手とおぼしき人たちが出艇してきた。吹き出したのか? 吹くのか? ボーナスタイムか? もう一度、沖へでてみたい気持ちにかられたけれど、どうせプレーニングできない、といいきかせて着岸。
ふりかえると、5艇ほどが沖で止まっていた。
ノープレーニングノーライフ、ふたたび。
というわけで。
以前つくったっきりだった速度リスト表示プログラムをupdate。
なんだかんだと不満な点もあったので。
JOSMで航跡を表示できるようにした。
あと、現在のトラックポイントからひとつ前の速度のトラックポイントへジャンプする機能を追加1。ほか多少。
ワンキーで最高速のトラックポイントへジャンプする機能は以前通り。
サーマルを期待して検見川浜へ。
ほんとうは本栖湖へ行くつもりだったのだが、朝起きれず。
いちおう吹いていた。カイト、デカスラ、フォイルなら走る風——それでもスラロームは走ったり、止まったりしている。
しばらく海を眺めていたら縦横無尽に走っているカイトがひとり。やたら速い。なんじゃあ、と思っていたらカイトフォイルだった。すげー速い。あっという間に沖まで上っていってしまった。
あれ、いいな。
駐車場でうだうだ。
夕方に吹いてきた感じがあり、海を見ると、さっきより走っているし、うみほたるの風速の実況は11m。これはいけるでしょ、と思ってボードをセッティングして波打ち際まで。
が、思ったよりない。
出艇しているセイルは小さいような気がするけど、きっと7.0m2はあるはず。最近のセイルはどれも小さく見えてしまうんだよね。
30分ほど、波打ち際で太陽に照らされて海を見ていたけれど——これはだめだ、とあきらめて撤収。
ノープレーニングノーライフ。
まったく。
lispでよく書くパターン。
(let ((w (XXX XXXX)))
(when w (BODY...))
というもので、取得した値があったら処理するというパターン。
これこそマクロにするべきものだよな。
名前は「when-let」あたりか。
と思ったところではっ、と気づいて調べてみると、elispにはまさにそれがあった……。
(require 'subr-x)
いったん動いているプログラムはあまり、いじりたくない。
elispならとくにそうで、プログラムがEmacsで動作するのでロードエラーがでるような状態にしてしまうと、通常の業務1に支障がでてしまう。というわけで、なおしたいなぁ、と思っていても放っておいてしまっていた。
でもgitのbranchを使えば、いいんじゃね?
新しいbranchを切ってそちらで修正をしておいて必要になったら。
git checkout master
で、元にもどせば、いい。
同じようなことはsubversionでもできたはずなのだけど、全然、思いつきもしなかった。何をやっておるのか。
というわけでこれからがんがん、プログラムがいじれる。
いじれるはず。
いじらないけど。
何の業務だ?
REPLって便利なんだけど、Emacsのscratchバッファになれているせいか、どうも使いづらい。scratshバッファは平面だけど、REPLは一次元ということがあるのだろう。
ダミーのlispファイルをひらいてそのバッファでコードを書いて——実際の動作はREPLで確認、ということをやっていた。
slimeの関数を眺めていたらslime-scratchというのを見つけた。
おっ。
スターン数列は,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までにしたら延々、もどってこない……。
もっと早くに読んでおくべきだった。
交互リストによるplistのことも書かれているし、keywordについても——CommonLispをはじめていろいろ疑問に思ったことの答えが書かれていた。
もっともずっと以前に読んでいたらこれらのことは気づかずにふーんとスルーしていたにちがいないけど。
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))
なんじゃ?
setfできるassocがalexandriaにあるらしい。
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で変更できるけど。
| No | 時刻 | 時間 | 帆走距離 | 最高速 | 平均時速 | P% |
|---|---|---|---|---|---|---|
| 1R | 14:21-14:43 | 21分 | 2.9km | 19.92km/h | 7.85km/h | 3.87 |
| 2R | 15:03-15:32 | 28分 | 4.1km | 31.47km/h | 8.56km/h | 5.89 |
| 3R | 16:04-16:17 | 13分 | 1.3km | 17.55km/h | 5.43km/h | 0.43 |
total 64分 8.2km 7.6km/h
ほんとうは海へ行くつもりはなかった。
GPVを見るかぎり、昨日より吹くとは思えない。昨日ですら微妙だったのだ。だから昨日、検見川へ行ったのに、今日も来てしまった。入庫待ちの車の列に並ぶ。さすがに昨日よりも早めだったので時間がかかってしまう。
風は微妙。いや無理だろう。
デカスラは走っているけれど。
夕方のひと吹きを期待して海へでる。だめ。腹が立つのは自分が海からあがると、急に吹き出して走り出したように見えることだ。もちろん錯覚だ。そもそもプレーニングしている人間と自分が同じ腕前という前提がまちがっている。
というわけで本日、プレーニングしたのは一本だけだった。
| No | 時刻 | 時間 | 帆走距離 | 最高速 | 平均時速 | P% |
|---|---|---|---|---|---|---|
| 1R | 14:45-15:02 | 17分 | 1.5km | 31.23km/h | 5.07km/h | 3.19 |
| 2R | 15:06-15:18 | 12分 | 1.3km | 19.62km/h | 6.60km/h | 2.28 |
| 3R | 15:44-16:50 | 65分 | 8.4km | 35.21km/h | 7.74km/h | 8.88 |
| 4R | 17:07-17:23 | 16分 | 2.5km | 34.69km/h | 9.07km/h | 13.72 |
total 111分 13.8km 7.4km/h
30分待ちで駐車場に入れることができた。
途中、道路から見える検見川浜は沖でデカスラっぽいセイルが走っていた。微妙に足りないんだろうなぁ、と思っていたら案の定で微妙に足りない風——ブローも微妙。
スタボーはうねりにあわせてなんとか、プレーニングすることもあったけれど。ポートは乗り方を忘れていることもあってほぼ全滅。それでも二度ほどプレーニングした。
ジャイブは、何それ、おいしいの? 状態。
二時すぎにあった風はいったん落ちて五時ぐらいにやや強くなる。
気温が下がって風が重くなったのかもしれない。
夏の検見川浜のサーマルははじめてだったけれど、もうすこし大きめのサイズがないときびしいなぁ。
それにしても身体が動かない。
晩年の岡部幸雄のようにストレッチをした方がいいんだろうなぁ。
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。
shellもできるか……。
けっこう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>
どうしてか、Lispには型がないとばかり思っていた。
さすがに文字列と数値とかはあるだろうけど、と。
DEF> (type-of :ABC) KEYWORD DEF>
ってやってみて驚いた。
「:」付きってkeywordという型をなのか。
あるじゃないか。型。
以前からよくわからなかった「:」はkeywordという型指定ということになるのか、つまり。もしかしたらリーダーマクロで実装されているんだろうか?
というか、根本的に勘違いしているじゃないか。
Lispはプログラムの実行時まで呼び出すべき変数のデータ型や関数の実体が決まらない動的束縛(late bindingともいう)の言語である。
竹内郁雄「初めての人のためのLisp [増補改訂版]」
たとえば、次のような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なので同じ名前が存在しても競合する可能性はきわめて低いから。
Beelink S1とMacBook Airとのソースの同期はどうしよう。
一発目は単純にscpしたけれど、これから色々とめんどうな予感がする。rsync?
ああ、そうか。どうせMacBook Airのソースはgitで管理しよう、と思っていたんだった。それならBeeLink S1からgit cloneしてやれば、いいのか。