2010年2月4日木曜日

cl-ppcreでマッチした文字列に$1などとしてアクセスする

cl-ppcreで正規表現にマッチした文字列を自動的に変数に代入してしまおうというネタ。きっと多くの人が一度はやったことあるに違いない。あるいはやる価値を感じていないか。

register-groups-bindのほうがどう考えても便利だけど、見た目で他の言語のユーザを引きつける事が出来る・・・かも。

(cl-interpol:enable-interpol-syntax)
(defparameter $0 nil)

;;;mkstr,symbはOnLispのもの。
(defun mkstr (&rest args)
(with-output-to-string (s)
(dolist (a args)
(princ a s))))

(defun symb (&rest args)
(values (intern (apply #'mkstr args))))

(defun =~ (regexp str &rest options)
(multiple-value-bind (start end reg-start-array reg-end-array)
(apply #'cl-ppcre:scan regexp str options)
(when (and (numberp start) (numberp end))
(set (symb "$0") (subseq str start end))
(loop
:for s across reg-start-array
:for e across reg-end-array
:for i from 1
:do
(set (symb "$" i)
(subseq str s e))))
(values start end reg-start-array reg-end-array)))

以下、使用例。

CL-USER> (=~ #?/(\d{4})\/(\d{2})\/(\d{2})/ "2010/02/04")
0
10
#(0 5 8)
#(4 7 10)
CL-USER> (list $0 $1 $2 $3)
("2010/02/04" "2010" "02" "04")

問題は値の代入が(symbol-value $0)に対して行われること。 letなどでレキシカル変数を使っていると意図した挙動にならないかもしれない。

CL-USER> (let ($0 $1 $2 $3 $4)
(=~ #?/(.{2})(.{3})(.{4})(.*)/ "abcdefghijklmnopqr")
(list $0 $1 $2 $3 $4))
("abcdefghijklmnopqr" NIL NIL NIL NIL)
CL-USER> (list $0 $1 $2 $3 $4)
(NIL "ab" "cde" "fghi" "jklmnopqr")

・・・ってあれ、なんで$0だけこうなるんだろう。

そうか、defparameterのせいだ。消しとこう。

;;;(defparameter $0 nil)を消した
CL-USER> (let ($0 $1 $2 $3 $4)
(=~ #?/(.{2})(.{3})(.{4})(.*)/ "abcdefghijklmnopqr")
(list $0 $1 $2 $3 $4))
(NIL NIL NIL NIL NIL)
CL-USER> (list $0 $1 $2 $3 $4)
("abcdefghijklmnopqr" "ab" "cde" "fghi" "jklmnopqr")

0 件のコメント:

コメントを投稿