以前から、Lisp でのデザインパターンとは何かについて考えていました。それに答えを与えてくれそうな 「On Lisp」 という本を読んでいます。
- 作者: ポールグレアム,野田開,Paul Graham
- 出版社/メーカー: オーム社
- 発売日: 2007/03/01
- メディア: 単行本
- 購入: 10人 クリック: 146回
- この商品を含むブログ (128件) を見る
quote の副作用
長年 Lisper をやってきたのに、quote で作られたリストを返す関数に副作用があることを、恥ずかしながらこの本で初めて知りました。
たとえば、以下のような関数です。
(defun foo(expr) (append expr '(a b)))
実行してみましょう。
(foo '(1 2 3)) ;; => (1 2 3 a b)
さてここで、foo の返り値に破壊的な関数 nconc を使ってみます。
(nconc (foo '(1 2 3)) '(c)) ;; => (1 2 3 a b c)
すると、挙動が変わります。
(foo '(1 2 3)) ;; => (1 2 3 a b c)
がーん。
quote で作られるリストは静的だからですね。解決するには、リストを list で動的に作ればいいそうです。
(defun bar(expr) (append expr (list 'a 'b))) (bar '(1 2 3)) ;; => (1 2 3 a b) (nconc (bar '(1 2 3)) '(c)) ;; => (1 2 3 a b c) (bar '(1 2 3)) ;; => (1 2 3 a b)
勉強になるなぁ。
back quote
少なくとも Emacs Lisp では、"," や ",@" を使わない back quote は quote に等価のようです。
(defun foo(expr) (append expr `(a b))) (foo '(1 2 3)) ;; => (1 2 3 a b) (nconc (foo '(1 2 3)) '(c)) ;; => (1 2 3 a b c) (foo '(1 2 3)) ;; => (1 2 3 a b c)
"," や ",@" を使うと、リストが動的に作られるようです。
(defun bar(expr) `(,@expr a b)) (bar '(1 2 3)) ;; => (1 2 3 a b) (nconc (bar '(1 2 3)) '(c)) ;; => (1 2 3 a b c) (bar '(1 2 3)) ;; = (1 2 3 a b)