インライン関数とは、ある一点を除けば、通常の関数と同様に動作する関数のことです。その一点とは、関数呼び出しをバイトコンパイルする際(バイトコンパイルを参照)に、関数の定義が呼び出し元に展開されるという点です。
インライン関数を定義する簡単な方法は、defun の代わりに defsubst と記述することです。定義の残りの部分は同じですが、defsubst と記述することで、バイトコンパイル用にインライン化するよう指定できます。
このマクロはインライン関数を定義します。その構文は defun とまったく同じです(関数の定義を参照してください)。
関数をインライン展開すると、その関数呼び出しの実行速度が向上することが多いです。しかし、デメリットもあります。まず、柔軟性が低下します。関数の定義を変更しても、すでにインライン展開されている呼び出しは、再コンパイルするまで古い定義のままとなります。
もう一つのデメリットは、大規模な関数をインライン化すると、ファイルサイズとメモリ使用量の両方でコンパイル済みコードのサイズが増大する可能性があることです。インライン関数による速度上のメリットは小規模な関数において最も大きいため、一般的に大規模な関数をインライン化すべきではありません。
また、インライン関数は、デバッグ、トレース、およびアドバイジングの観点からは使い勝手が良くありません(Emacs
Lisp関数のアドバイジングを参照)。デバッグのしやすさや関数を再定義できる柔軟性はEmacsの重要な機能であるため、たとえ小規模な関数であっても、速度が極めて重要であり、かつdefunの使用が実際にパフォーマンス上の問題を引き起こすことを計測で確認していない限り、インライン関数にするべきではありません。
インライン関数を定義すると、マクロと同様に、同じファイル内の後続箇所でそのインライン展開を行うことができます。
defmacro
を使用して、インライン関数が実行するのと同じコードに展開されるマクロを定義することが可能です(マクロを参照)。ただし、このマクロは式内での直接使用に限定されます。つまり、apply や mapcar
といった形式でマクロを呼び出すことはできません。また、通常の関数をマクロに変換するには多少の手間がかかります。インライン関数に変換するのは簡単で、単に
defun を defsubst
に置き換えるだけです。インライン関数の各引数は正確に1回だけ評価されるため、マクロの場合とは異なり、関数本体で引数が何回使用されるかを心配する必要はありません。
あるいは、コードを指定して関数を定義することで、それをコンパイラマクロとしてインライン展開させることも可能です(declare
形式を参照)。以下のマクロを使用することで、これが可能になります。
コンパイラマクロとして、インライン展開を行うコードを提供し、関数 name を定義してください。この関数は引数リスト args を受け取り、指定された body を持ちます。
doc が存在する場合、それは関数のドキュメント文字列である必要があります(関数のドキュメント文字列を参照)。declare が存在する場合、それは関数のメタデータを指定する declare 形式(declare
形式を参照)である必要があります。通常の宣言に加えて、非 nil の NOINLINE によって Emacs が定義済み関数をインライン展開できなくなる場合、declare に (noinline NOINLINE) を含めることができます。
define-inline で定義される関数は、defsubst や defmacro
で定義されるマクロに比べて、いくつかの利点があります:
mapcar に渡すことができます(マッピング関数を参照)。
cl-defsubstよりも予測可能な挙動を示します(Common Lisp Extensions for GNU Emacs Lispの引数リストを参照)。
defmacroと同様に、define-inlineでインライン展開された関数は、呼び出し箇所から動的またはレキシカルなスコープ規則を継承します。変数束縛のスコープ規則を参照してください。
以下のマクロは、define-inline で定義された関数の本体で使用する必要があります。
define-inline に対して expression をクォートします。これはバッククォート(バッククォート を参照)に似ていますが、コードをクォートし、,@ ではなく , のみを受け付けます。
これにより、インライン関数への引数が正確に1回だけ評価されることを保証できるほか、ローカル変数を作成することも容易に行えます。
これは let と同様です(ローカル変数 を参照):bindings
で指定されたローカル変数を設定し、それらの束縛が有効になった状態で body を評価します。
bindings の各要素は、シンボルか (var expr)
形式のリストのいずれかである必要があります。その結果、expr を評価し、var をその結果にバインドします。ただし、bindings
の要素が単なるシンボル var である場合、var
の評価結果が var に再バインドされます(これは let の動作とは大きく異なります)。
bindings の末尾は、nil
または引数のリストを保持するシンボルのいずれかとなります。後者の場合、各引数が評価され、そのシンボルは結果として得られたリストにバインドされます。
expressionの値が既に判明している場合は、nil以外を返す。
expressionの値を返します。
エラーを通知し、args を format に従ってフォーマットする。
define-inlineの使用例を以下に示します。
(define-inline myaccessor (obj)
(inline-letevals (obj)
(inline-quote (if (foo-p ,obj) (aref (cdr ,obj) 3) (aref ,obj 2)))))
これは以下と等価である。
(defsubst myaccessor (obj) (if (foo-p obj) (aref (cdr obj) 3) (aref obj 2)))