僕は Lisper ですが、「Lisp では、マクロを書けばなんでもできる」という主張には、かなり疑問を持っていました。
たとえば、「Emacs Lisp で総称関数を導入することなんてできない」と思っていたのです。しかし、今日ひらめいて、試しにこんなコードを書きました。
(defmacro defmethod (func <type> args &rest body) `(let ((type (intern-soft (concat (substring (symbol-name ',<type>) 1 -1) "p")))) (unless (fboundp ',func) (fset ',func (lambda (data &rest params) (catch 'loop (dolist (predicate-lambda (get ',func 'method)) (if (funcall (car predicate-lambda) data) (throw 'loop (apply 'funcall (cdr predicate-lambda) data params)))))))) (put ',func 'method (cons (cons type (lambda ,args ,@body)) (get ',func 'method))) ',func))
この defmethod を使うと、以下のようにリストに対して mapper を定義できます。
(defmethod mapper <list> (lst func) (mapcar func lst))
定義した mapper にリストを与えてみると、こんな風に動きます。
(mapper '(1 2 3 4) '1+) ;; => '(2 3 4 5)
次にベクトルに対して、mapper を定義してみます。
(defmethod mapper <vector> (vec func) (let ((ret (copy-sequence vec))) (dotimes (i (length ret) ret) (aset ret i (funcall func (aref ret i))))))
以下のようにちゃんと動きます。
(mapper [1 2 3 4] '1+) ;; => [2 3 4 5]
まったく同じで申し訳ありませんが、文字列に対しても mapper を定義してみましょう。
(defmethod mapper <string> (str func) (let ((ret (copy-sequence str))) (dotimes (i (length ret) ret) (aset ret i (funcall func (aref ret i))))))
以下のように、ちゃんと動きました。
(mapper "abcd" '1+) ;; => "bcde"
Lisp って、すごーい。(今頃気付くな!:)