関数を定義するのは、まだ道のりの半分に過ぎません。関数は、呼び出し、つまり実行を指示するまで何もしません。関数を呼び出すことは、呼出しとも呼ばれます。
関数を呼び出す最も一般的な方法は、リストを評価することです。例えば、リスト (concat
"a" "b") を評価すると、引数 "a" と "b"
を指定して関数 concat が呼び出されます。評価の説明については、評価 を参照してください。
プログラム内で式としてリストを記述する場合、どの関数を呼び出し、いくつの引数を渡すかをプログラムの本文中で指定します。通常はこれで十分です。場合によっては、実行時にどの関数を呼び出すかを計算する必要があります。そのためには、関数
funcall を使用してください。また、渡す引数の数も実行時に決定する必要がある場合は、apply を使用してください。
funcallはargumentsを指定してfunctionを呼び出し、functionが返す値をそのまま返します。
funcall は関数であるため、funcall
が呼び出される前に、function
を含むすべての引数が評価されます。つまり、任意の式を用いて、呼び出す関数を得ることができます。また、funcall は arguments
に対して記述した式を認識せず、その値のみを参照します。これらの値は、function
を呼び出す際に二度目の評価を受けることはありません。funcall の動作は、引数がすでに評価された後の通常の関数呼び出し手順と同様です。
引数 function
は、Lisp関数またはプリミティブ関数のいずれかである必要があります。特殊形式やマクロは、未評価の引数式が与えられた場合にのみ意味をなすため、許可されません。前述の通り、funcall はそもそもこれらを知ることがないため、提供することはできません。
funcall
を使用してコマンドを呼び出し、対話的に実行したかのように動作させる必要がある場合は、funcall-interactively を使用してください(対話的呼び出しを参照)。
(setq f 'list)
⇒ list
(funcall f 'x 'y 'z)
⇒ (x y z)
(funcall f 'x 'y '(z))
⇒ (x y (z))
(funcall 'and t nil) error→ Invalid function: #<subr and>
これらの例を apply の例と比較してください。
apply は function を arguments と共に呼び出します。これは funcall
と同様ですが、一点だけ異なります。arguments
の末尾はオブジェクトのリストになっており、単一のリストとしてではなく、個別の引数として function
に渡されます。これを、apply がこのリストを展開し、個々の要素がそれぞれ引数になると言います。
単一の引数を取る apply
は特殊な挙動を示します。空でないリストである必要がある引数の第1要素は、残りの要素を個別の引数として関数呼び出しされます。2つ以上の引数を渡す方が高速になります。
apply は function
を呼び出した結果を返します。funcall と同様に、function は Lisp 関数またはプリミティブ関数である必要があります。特殊フォームやマクロは apply では意味をなしません。
(setq f 'list)
⇒ list
(apply f 'x 'y 'z) error→ Wrong type argument: listp, z
(apply '+ 1 2 '(3 4))
⇒ 10
(apply '+ '(1 2 3 4))
⇒ 10
(apply 'append '((a b c) nil (x y z) nil))
⇒ (a b c x y z)
(apply '(+ 3 4))
⇒ 7
apply の使用例としては、mapcar の定義を参照してください。
関数の引数の一部を特定の値に固定し、残りの引数を実際に関数が呼び出される際まで残しておくことが有用な場合があります。関数の引数の一部を固定する操作を、その関数の部分適用と呼びます16。その結果、残りの引数を受け取り、すべての引数を組み合わせて元の関数を呼び出す新しい関数が得られます。
Emacs Lispでは、匿名関数を使うのが最適です。例えば、2つの引数を取る関数 ‘my-function’ がある場合、次のように記述できます。
(mapcar (lambda (x) (my-function 123 x)) ...)
また、apply-partially
関数を使って部分的な適用を行うこともできます。ただし、lambda
で無名関数を使う場合よりも処理速度は遅くなります。
この関数は、呼び出された際に、args と呼び出し時に指定された追加引数から構成される引数リストを用いて
func を呼び出す新しい関数を返します。func が
n 個の引数を受け取る場合、m <= n 個の引数を用いて
apply-partially を呼び出すと、n - m
個の引数を持つ新しい関数が生成されます17。
組み込み関数 1+ が存在しない場合に、apply-partially と別の組み込み関数 +18
を用いてそれを定義する方法は以下の通りです。
(defalias '1+ (apply-partially '+ 1) "Increment argument by one.")
(1+ 10)
⇒ 11
Lispの関数が、引数として関数を受け取ったり、データ構造(特にフック変数やプロパティリスト)の中から関数を探し出してfuncallやapplyを用いて呼び出したりすることはよくあります。関数を引数に取る関数は、しばしばファンクショナルと呼ばれます。
関数を呼び出す際、引数としてno-op関数を渡すと便利な場合があります。以下に、3種類の異なるno-op関数を紹介します。
この関数は argument を返し、副作用はありません。
この関数は arguments を無視し、nil
を返します。プレースホルダーとして使用する場合(一般化変数 を参照)、代入は一切効果を持ちません。
この関数は arguments を無視し、t
を返します。
一部の関数はユーザーから見えるコマンドであり、対話的に(通常はキーシーケンスによって)呼び出すことができます。call-interactively関数を使用することで、このようなコマンドをあたかも対話的に呼び出した場合と全く同様に実行することが可能です。対話的呼び出しを参照してください。
これは、複数の引数を取る関数を、それぞれ単一の引数を持つ関数の連鎖として呼び出せるように変換するカリー化に関連していますが、それとは異なります。
func
が受け付けられる引数の数が無制限である場合、新しい関数も無制限の数の引数を受け付けるため、その場合 apply-partially は新しい関数が受け付けられる引数の数を減らすことはありません。
なお、組み込み関数とは異なり、このバージョンでは任意の数の引数を受け付けます。