sharp-backquoteとsharp-lambda
Let Over Lambdaの6章に『#`』というリーダマクロが出てきます。
バッククォートされたものを返す関数を作るためのもので、以下のように定義されています。
(defun |#`-reader| (stream sub-char numarg) (declare (ignore sub-char)) (unless numarg (setq numarg 1)) `(lambda ,(loop for i from 1 to numarg collect (symb 'a i)) ,(funcall (get-macro-character #\`) stream nil)))
これをset-dispatch-macro-characterで登録してやれば、
次のような感じに使えます。
CL-USER> ‘#`(car ,a1)
(LAMBDA (A1) `(CAR ,A1))
CL-USER> (mapcar #`(,a1 (gensym)) ‘(x y z))
((X (GENSYM)) (Y (GENSYM)) (Z (GENSYM)))
ちょっと便利そうだけど、そこまで使うのか?
と少し疑問に思いましたが、今までに自分が書いたソースを見直してみると、
(lambda (x) `(…簡単な式…))
というパターンが結構ありました。これを使うとかなり簡潔に書けそうです。
しかし、バッククォートで囲わないで、生のlambda式を作るリーダマクロの方が
むしろ便利ではないのかと思い、作ってみました。
(defun |#^-reader| (stream sub-char numarg) (declare (ignore sub-char)) (unless numarg (setq numarg 1)) `(lambda ,(loop for i from 1 to numarg collect (symb '$ i)) ,(read stream t nil t)))
lambda式のsyntax sugarということで、『#^』(sharp-lambda)と命名しました。
引数がa1, a2, …となるのは個人的に気に入らなかったので、
$1, $2, …という名前に変えました。この方が所見の人もきっと分かりやすいはず。多分。
CL-USER> '#^(car $1) (LAMBDA ($1) (CAR $1)) CL-USER> (mapcar #^(car $1) '((a b) (c d) (e f))) (A C E)
$0でその関数自身をさせるようにしてもいいかも知れません。
(lambdaの代わりにlabelsを使えばすぐにできますし。)
#^を使ってlambda式を書き直していくと、ソースがかなり簡潔になりました。
これはかなり便利かも。ただ、他人が読みにくくなるという問題がありますね。
私は、lambdaと入力するのはさほど手間に思っていません。
‘l’をタイプすると自然に指が残りの文字をタイプしてくれますし、
どうしても面倒ならエディタの補完機能を使えば全く手間がかかりません。
私が面倒に思うのはlambdaに伴う括弧です。
lambda自身を囲む括弧に、引数を囲む括弧。
「括弧は友達、怖くないよ」とはいえ、何度も打ってると面倒になってきます。
ちょうど一年前、フムフムヌクヌクアプアア本でcut(srfi-26)をはじめて知ったときは、
かなり気持ち悪く見えたのに、今ではこういったものが必要不可欠にみえてきます。
この悲惨なキーボードを修理もせずに使い続けた結果、
括弧を減らせるマクロを求めるようになってきたのかもしれませんが(笑)