あどけない話

インターネットに関する技術的な話など

Emacs Lisp と総称関数

僕は 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 って、すごーい。(今頃気付くな!:)