QUOTEとFUNCTIONを使い分ける:もう少し詳しい説明 - lisp-cookbook-ja/common-lisp GitHub Wiki

  • QUOTEとFUNCTIONを使い分ける:もう少し詳しい説明

QUOTEとFUNCTIONについてもう少し踏み込んだ説明をすると、

  • FUNCTION特殊形式はシンボルに関係付けられた関数オブジェクトを返す
  • QUOTE特殊形式はシンボルそのものを返す

という違いです。したがって、(apply #'foo ...) ではapplyが受け取るのは「関数オブジェクト」であるのに対し、(apply 'foo ...) では単なるシンボルです。 単なるシンボルを渡された場合に、symbol-functionで大域関数を取り出す、という操作はapplyやfuncallがやってくれる仕事です。

関数を取り出す動作がひと手間増えるため#'fooの方が若干速くなることもありますが、大抵の場合、(funcall 'foo)と、(funcall #'foo)に違いはないでしょう。

挙動に違いがでる場合

  • functionでないとローカル関数は呼べない
(labels ((foo () :hello))
  (funcall 'foo))
;>>> エラー


(labels ((foo () :hello))
  (funcall #'foo))
;=>  :HELLO

ローカル関数は、functionでないと参照することはできず、クォートでは大域関数を呼ぶことしかできません

  • 変数に保存した場合

#'foo という形式では、関数オブジェクトを取り出しますので、変数に保存すれば、その時点の関数が保存されます。関数オブジェクトは、名前とは関連づけられていませんので、同名で再定義しても影響はありません。

一方 'fooは単なる名前ですので、呼び出し時点の関数を引いてくることになり、過去の状態を保存することはできません

(defun xyzzy () (princ "1"))
;=>  XYZZY


(defvar *f1* #'xyzzy)
;=>  *F1*


(defvar *q1* 'xyzzy)
;=>  *Q1*


;;; 再定義
(defun xyzzy () (princ "2"))
;=>  XYZZY


(defvar *f2* #'xyzzy)
;=>  *F2*


(defvar *q2* 'xyzzy)
;=>  *Q2*
(funcall *f1*)
;->  1
;=>  "1"


(funcall *q1*)
;->  2
;=>  "2"


(funcall *f2*)
;->  2
;=>  "2"


(funcall *q2*)
;->  2
;=>  "2"
  • 関数をインライン化し、それをfunctionで呼んだ場合(処理系依存)
(declaim (inline fn))
(defun fn (x) :hello)


(defun test-quote ()
  (mapcar 'fn (make-list 5)))


(defun test-function ()
  (mapcar #'fn (make-list 5)))


(test-quote)
;=>  (:HELLO :HELLO :HELLO :HELLO :HELLO)

(test-function)
;=>  (:HELLO :HELLO :HELLO :HELLO :HELLO)

(defun fn (x) :goodbye)

(test-quote)
;=>  (:GOODBYE :GOODBYE :GOODBYE :GOODBYE :GOODBYE)

(test-function)
;=>  (:HELLO :HELLO :HELLO :HELLO :HELLO)

※インライン化は、処理系によりされる場合とされない場合がありますので、この動作は、処理系依存になります

functionを利用して記述した場合、関数であることは確実ですので、コンパイル時にインライン展開されます。

このため、fnの再定義されても、test-functionが再度コンパイルされfnのインライン展開がされないと元の定義が埋め込まれたままになります。