また、iPadProの影響で背筋を痛めそうだったのでタブレットスタンドというやつを買った。
これで寝転がっていろいろできるぜ、と思っていたのだけれど。
タップしたり、スワイプしたりするたびに、びよ〜〜んびよ〜〜んとiPadが揺れる。おかげですぐに船酔い状態。気分が悪くなってしまった。
あかん。
「Land of Lisp」ではじめてfletというものを知った。
letの関数版。関数内にローカルの関数をつくれるものらしい。elispにもcl.elにあった。同じようなことはletの変数にlambdaをセットしてfuncallしてやってみたことはあるけれど。
便利そうだけど、あんまり使わないかも、とか、思っていたら。
そういやつくった関数に何か皮をかぶせた処理をいれたい、とか、よくあることだった。そういうときは別の関数をつくって皮をかぶせていたけれど、fletに押し込んでやれば、いいのか……。
なるほど。
で、fletの*HELP*を読んでいたらletfというものがあることを知る。
(setq wrk '(1 2 3)) (1 2 3) (cl-letf (((nth 1 wrk) 44)) (nth 1 wrk)) 44 wrk (1 2 3)
おっ、すげえ、と思ったけれど。
(cl-letf (((nth 1 wrk) 44)) wrk) (1 2 3)
となってしまった。
なんとなく、(1 44 3)が返ってくるような気がしていたのだけど。
もしかしたら(nth 1 wrk)の部分を44に差し替えているだけなのかな? と思ってmacroexpandしてみたら。
(macroexpand '(cl-letf (((nth 1 wrk) 44))
(nth 1 wrk)))
(let* ((c (nthcdr 1 wrk))
(old (car c)))
(unwind-protect
(progn (setcar c 44)
(nth 1 wrk))
(setcar c old)))
(macroexpand '(cl-letf (((nth 1 wrk) 44))
wrk))
(let* ((c (nthcdr 1 wrk)) (old (car c)))
(unwind-protect (progn (setcar c 44)
wrk)
(setcar c old)))
あれ?
最初のやつは44が返ってくるのに、なんで二番目のやつは(1 44 3)が返ってこないんだろう……? というか、CommonLispにはデフォルトでletfってないのかな。「undefined function: LETF」になってしまったけど。
パソコンを長々と使っていると、いろいろと作業を自動化したいわけで。
よくちょこちょことスクリプトを書いたりしていた。
shellスクリプトからsed&awkを経てperlに至り。途中、ruby1をつまみ食いしたりして。DOSバッチはUNIXツールのパチモンをいれていたのでawkやperlスクリプトという感じ。さらにtcl/Tkも使ったことがあった。これは画面がつくれたのがよかった。
そうこうしているうちに。
一番使うのはEmacsだったこともあってelispを勉強しはじめ2。それでもperlやtcl/Tk、shellを使っていたのだけど、ある日突然、elispで充分じゃね、と気づいてからはelisp一本やり。3以来、elisp以外はほとんと使っていない。
CommonLispやschemeをインストールしてみたりしているけれど、なんだかんだとelispで充分で、どうやら個人的なスクリプト環境はナッシュ均衡に逹っしてしまったらしい。
文字列と数値が混在していてつねに数値としてあつかいたいのに〜、と苛々することがよくあったので下記のような関数をでっちあげた。数値としてあつかいたいとき、無条件に使うわけである。
(defun y-num(x) (cond ((null x) 0) ((numberp x) x) ((stringp x) (string-to-number x)))) (y-num "123") 123
ずっとそうやって使っていたんだけど、ふとlistも対応しよう、と思って対応してみたところ。
(defun y-num(x)
(cond ((null x) 0)
((numberp x) x)
((stringp x) (string-to-number x))
((listp x)(mapcar 'y-num x))))
(y-num '("1" "2" "3"))
(1 2 3)
あっ、と思った。これだけで入れ子のlistでもOKじゃん。
(y-num '("1" ("1" "2" "3") "2" "3"))
(1 (1 2 3) 2 3)
再帰ってすげー、と思って。
いやいや、そうか、と思い直した。
Lispで再帰がよく使われるのはlistが基本構造なので、むちゃくちゃ相性がいいからなんだ。
最近、読んだ「Land of Lisp」の中に遅延プログラミングがでてきた。
ポール・グラハムの「On Lisp」にもあったよなぁ1、とか、考えていてふと気づいた。レキシカルスコープがサポートされたんだからelispでも可能なんじゃね?
試してみた2。
;; -*- lexical-binding: t; -*-
(defmacro lazy(body)
(let ((forced (gensym))
(value (gensym)))
`(let ((,forced nil)
(,value nil))
(lambda()
(unless ,forced
(setf ,value (progn ,body))
(setf ,forced t))
,value))))
(defun force(lazy-value)
(funcall lazy-value))
(defmacro lazy-cons(a d)
`(lazy (cons ,a ,d)))
(defun lazy-car(x)
(car (force x)))
(defun lazy-cdr(x)
(cdr (force x)))
(defun lazy-nil()
(lazy nil))
(defun lazy-null(x)
(null (force x)))
(defun make-lazy(lst)
(lazy (when lst
(cons (car lst)(make-lazy (cdr lst))))))
(defun take(n lst)
(unless (or (zerop n)(lazy-null lst))
(cons (lazy-car lst)(take (1- n)(lazy-cdr lst)))))
(defun take-all(lst)
(unless (lazy-null lst)
(cons (lazy-car lst)(take-all (lazy-cdr lst)))))
いちおう動いているみたいだ。
注意。「lazy」はレキシカルスコープの環境で動かすこと。考えてみれば、当たり前なんだけど、最初、うまく動かなくて考えこんでしまった。lazyの定義はレキシカルスコープでやって、実行をダイナミックスコープ環境でやっていたので。closureになってなかった。
それにしてもcl.elでlabelsも使えるんだ。知らんかったわ。
;; -*- lexical-binding: t; -*-
(setq wrk (labels ((f (n)
(lazy-cons n (f (1+ n)))))
(f 1)))
(take 10 wrk)