Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2007-10-12

マクロでPerlのmyのまね

| 00:06 | マクロでPerlのmyのまね - わだばLisperになる を含むブックマーク はてなブックマーク - マクロでPerlのmyのまね - わだばLisperになる

Perlに挑戦して何度も挫折しているんですが、それならLispでPerlの機構を真似ることで学ぶというのはどうだろうかと思い立ち、Perlの入門書を見ながらPerlの機構を再現してみることにしました。

今回は、変数束縛機構?のmyを作ってみました。

Gaucheのlet1+Common Lispのdestructuring-bind+αみたいな感じになってます。

(defmacro my (vars vals &body body)
  (cond ((member vals '('nil nil) :test #'equal)
	 `(let ,(if (consp vars) vars `(,vars))
	    ,@body))
	('T 
	 (if (consp vars)
	     (if (member '&rest vars)
		 `(destructuring-bind ,vars ,vals
		    (declare 
		     (ignorable ,@(remove-if-lambda-list-keyword vars)))
		    ,@body)
		 (let ((g (gensym)))
		   `(destructuring-bind 
			  (,@(remove-if-lambda-list-keyword vars) &rest ,g) ,vals
		      (declare (ignorable ,@vars ,g))
		      ,@body)))
	     `(let ((,vars ,vals))
		,@body)))))

(defun remove-if-lambda-list-keyword (lst)
  (remove-if (lambda (x) (member x lambda-list-keywords)) lst))

;;; (my foo ()
;;;     (print foo))
;;; => NIL
;;; 
;;; (my foo 1
;;;     (print foo))
;;; => 1
;;; 
;;; (my (foo) '(1)
;;;     (print foo))
;;; => 1
;;; (my foo '(1 2 3 4 5 6 7 8 9)
;;;     (format t "~@{~A~^, ~}" foo))
;;; -> (1 2 3 4 5 6 7 8 9)
;;; 
;;; (my (foo bar baz) '(1 2 3 4 5 6 7 8 9)
;;;     (format t "~@{~A~^, ~}" foo bar baz))
;;; -> 1, 2, 3
;;; 
;;; (my (foo bar baz) '()
;;;     (format t "~@{~A~^, ~}" foo bar baz))
;;; -> NIL, NIL, NIL
;;; 
;;; (my (foo bar baz) '(1 2 3 4 5 6 7 8 9)
;;;     (format t "~@{~A~^, ~}" foo bar baz))
;;; -> 1, 2, 3
;;; 
;;; (my (foo bar baz) ()
;;;     (format t "~@{~A~^, ~}" foo bar baz))
;;; -> NIL, NIL, NIL
;;; 
;;; (my (foo bar &rest baz) '(1 2 3 4 5 6 7 8 9)
;;;     (format t "~@{~A~^, ~}" foo bar baz))
;;; -> 1, 2, (3 4 5 6 7 8 9)
;;; 
;;; (my (&rest foo) '(1 2 3 4 5 6 7 8 9)
;;;     (format t "~@{~A~^, ~}" foo))
;;; -> (1 2 3 4 5 6 7 8 9)

マクロ書法についての疑問

最近マクロを自分流でごちゃごちゃ作っていて思うのですが、展開時に色々処理をするというのは非常に悪いスタイルなのかもしれないと気になります。

Tutorial on Good Lisp Programming Style

http://norvig.com/luv-slides.ps

での、

Bad: Works at the expansion time.

(defmacro defclass (name &rest def)
  (setf (get name 'class) def)   
  ...
  (list 'quote name))

のようなものが念頭にあるのですが、今回のものも1通りに展開されるのではなくて、状況によって3通りに展開されます。

いまいち勘所が掴めないという…。

上記の例では、

  1. 展開時の動作で代入をしてしまっている。

ってのが要点でしょうか。展開の副作用を処理のメインに使うのは良くないという主張だとしたら何となく分かりますが、なかなか難しい…。