xyzzy用リーダーマクロ x 3

ちょっと前にリーダーマクロに関するtweetや記事が色々出てたので(発端はここ?)、普段xyzzyの*scratch*で使っているリーダーマクロを晒してみる。

#?= (debug-printリーダー)

Gauche's debug-print macro for CL
Gaucheの#?=劣化コピー。CLでも使用可。

> (list 0 1 #?=(values 2 3 4) 5 6)
#?=(values 2 3 4)
#?-    2
#?+    3
#?+    4
(0 1 2 5 6)

> (defun fact (n) 
    (if (= 0 n) 1
      (* n #?=(fact #?=(1- n)))))

> (fact 4)
#?=(fact (debug-print (1- n)))
#?=(1- n)
#?-    3
#?=(fact (debug-print (1- n)))
#?=(1- n)
#?-    2
#?=(fact (debug-print (1- n)))
#?=(1- n)
#?-    1
#?=(fact (debug-print (1- n)))
#?=(1- n)
#?-    0
#?-    1
#?-    1
#?-    2
#?-    6
24

本家のdebug-printはソース内の位置も表示してくれます。便利。

[ ], #n[ ] (無名関数リーダー)

brackets.lisp
[ ]はarcと同等、#[ ]は引数の数を指定できるバージョン(Let Over Lambdaに出てくるsharp-backquote-readerのタイプ量をちょっと減らした物という感じ)です。これもCLで使用可。

(require "brackets")
(brackets:enable-brackets)

> (defun my-subsetp (lst1 lst2 &key test)
    (every [find _ lst2 :test test] lst1))
my-subsetp

> (my-subsetp '(2 3) '(1 2 3 5 6))
t
> (my-subsetp '(2 3) '(1 2 4 8))
nil

> (mapcar #2[+ (* _1 _1) _2] '(1 2 3) '(4 5 6))
(5 9 15)

Gaucheのshorten lambdaも使ってるんだけど、こんなのを常時使ってるせいか[ ]の方が好み。
ちなみに#\[をmacro-characterにしてしまうと#\C-[を#\C-\[と書かなくてはいけなくなる等の影響があるので注意が必要です。
(~/lisp/以下のファイルには何も影響がなかった、はず ole.lがリロードできなくなります。要注意。)

#/re/ (Perl正規表現リーダー)

reut/psre-reader.l at master · youz/reut · GitHub
見た目はGuahceのregexpリーダーだけどこれはまあただの文字列変換器。perlrubyのノリで記述した正規表現xyzzy(Emacs由来?)の正規表現に変換します。
と言ってもメタ文字のエスケープの変更と\s(空白)や\d等が使えるようになる程度で、先読み等が使えるようになる訳ではありません。

> (and (string-match (print #/([\[\]\d]+)/) "asdf[42]")
       (match-string 1))
"\\([][0-9]+\\)" 
"[42]"

> (let ((nonprime (print #/^(11+)\1+$/)))
    (loop for i from 2 to 1000
      for s = "11" then (concat s "1")
      when (not (string-match nonprime s))
      sum 1))
"^\\(11+\\)\\1+$" 
168

元々は半年ほど前にLOLのコードについてlabelsとdo混ぜるな死ね的な事がどっかで書かれてたのを見て、いやいや混ぜて書けるのがいいんでね?と思ってでっち上げたコードでした。
使ってみたらいつもエスケープで混乱してた自分には存外便利で、せっかくだからとsearch-dialogとreplace-dialogにこの変換を噛ませる拡張も書いてみたりしたのだけど、よく考えたらboost/regex版perfrom-replaceを頑張って作った方がもっと幸せになれる事に気付いた。誰かやってくれてないかな。


以上3つは普段から使ってるんですが、これ*scratch*で使ってるって言うより配布等する可能性のあるコードでは使わないようにしてるってだけで、普通にシステム全体に影響あるのでとってもアレ。(brachetsだけはワンクッション置くように書いたけど)
もし導入を考える場合はご注意を。