[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11. 関数

Lispプログラムは、主にLisp関数で構成されます。この章では、関数と いう概念、引数の処理方法、関数の定義方法について説明します。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.1 関数とは

一般に、関数とは、引数(argument)と呼ばれる与えられたいく つかの値に対して計算を実行するための規則です。計算の結果は、関数 の値と呼ばれます。計算には、副作用がともなう場合もあります。副作 用には、変数の値の変化や、データ構造の内容の変化があります。

Emacs Lispの関数や関数に準ずるオブジェクトに関する重要な用語をいくつか説明 します。

関数

Emacs Lispでは、関数(function)とは、Lispプログラムにおい て引数に適用可能なものすべてを指します。場合によっては、Lispで書 かれた関数を特に意味する場合もあります。特殊形式やマクロは関数で はありません。

プリミティブ

プリミティブ(primitive)とは、carappendのよ うに、Cで書かれた、Lispから呼び出せる関数です。プリミティブは、 組込み(built-in)関数またはsubrとも呼ばれます(特殊 形式もプリミティブとみなされます)。

通常、関数をプリミティブにする理由には、関数が基本的なものである こと、オペレーティング・システムのサービスへの低レベル・インター フェースを実現するものであること、高速に実行する必要があること、 などがあります。プリミティブを修正したり追加するには、Cのソース を変更して、エディタをコンパイルしなおさなければなりません。 Emacsのプリミティブを書くを参照してください。

ラムダ式

ラムダ式(lambda expression)とは、Lispで書かれた関数です。 ラムダ式については、次に示す節で説明しています。

特殊形式

特殊形式(special form)は、関数に似ていますが、すべての引数 を評価する方法が通常の形式とは異なります。特殊形式は、一部の引数だけを 評価したり、通常とは異なる順番で引数を評価したり、同じ引数を何度 も評価する場合があります。制御構造では、たくさん の特殊形式について説明しています。

マクロ

マクロ(macro)とは、プログラマがLispで定義する構文要素です。マクロが関 数とは異なるのは、マクロは、プログラマが書いたLisp式を評 価せずに、そのまま等価な式に変換する点です。マクロを使えば、特殊形式と同 様のことをLispプログラムで実現できます。マクロの定義方法や使い方 については、See section マクロ

コマンド

コマンド(command)とは、command-executeが起動できるオブジェク トであり、通常はキー操作の定義内容として使われます。一部の関数は コマンドです。Lispで書かれた関数は、対話的コマンド宣言を含んで いればコマンドになります(see section コマンドの定義)。コマンド関 数は、ほかの関数と同様にLisp式から呼び出せます。その場合、その関数が コマンドであることに意味はありません。

キーボード・マクロ(文字列とベクタ)も、関数ではありませんがコマ ンドです。コマンドを関数定義にもつシンボルもコマンドです。このよ うなシンボルは、M-xで起動できます。同様に、シンボルの定義 が関数ならば、そのシンボルは関数です。See section コマンド・ループ概観

キーストローク・コマンド

キーストローク・コマンド(keystroke command)とは、キー・シー ケンス(通常は一つから三つのキーストローク) にバインドされたコマン ドです。キーストローク・コマンドとコマンドの違いをここで説明して いるのは、Emacs以外のエディタでの"コマンド"と混同しないように するためだけです。通常、Lispプログラムでは、キーストローク・コマ ンドとコマンドの違いは重要ではありません。

バイトコード関数

バイトコード関数(byte-code function)とは、バイトコンパイラ でコンパイルされた関数です。See section バイトコード関数型

Function: subrp object

この関数は、objectが組込み関数(Lispプリミティブ)ならば tを返します。

 
(subrp 'message)            ; messageはシンボルです。
     ⇒ nil                 ;   subrオブジェクトではありません。
(subrp (symbol-function 'message))
     ⇒ t
Function: byte-code-function-p object

この関数は、objectがバイトコード関数ならばtを返しま す。例を次に示します。

 
(byte-code-function-p (symbol-function 'next-line))
     ⇒ t

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.2 ラムダ式

Lispで書かれた関数は、次のようなリストです。

 
(lambda (arg-variables…)
  [documentation-string]
  [interactive-declaration]
  body-forms…)

このようなリストは、ラムダ式(lambda expression)と呼ばれて います。Emacs Lispでは、ラムダ式は実際には式として有効であり、評 価されるとそれ自体になります。一部のLisp方言では、ラムダ式は有効 な式ではありません。どちらの場合も、ラムダ式は式として評価するた めではなく、関数として呼び出すために主に使われます。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.2.1 ラムダ式の構成要素

ラムダ式の最初の要素は、つねにシンボルlambdaです。 lambdaは、リストが関数を表現していることを示します。関数 の定義をlambdaで始めるのは、ほかの用途に使われるリストを 誤って関数として有効にしてしまわないためです。

2 番目の要素は、シンボル (引数変数の名前) のリストです。このリス トは、ラムダ・リスト(lambda list)と呼ばれます。Lisp関数が呼 び出されると、ラムダ・リスト内の各変数に対して引数の値が対応つけら れます。そして、各変数には、渡された値がローカルに束縛されます。 See section ローカル変数

説明文字列は、関数定義内に置かれたLisp文字列オブジェクト で、Emacsヘルプ機能での関数の説明として使われます。See section 関数の説明文字列

interactive宣言は、(interactive code-string)と いう形式のリストです。interactive宣言は、関数が対話的に使わ れたときの引数の供給方法を宣言します。この宣言をもつ関数は、 コマンド(command)と呼ばれます。コマンドは、M-xを使っ て呼び出したり、キーにバインドできます。このような方法で(対話的 に)呼び出すべきでない関数には、interactive宣言を入れないで ください。interactive宣言の書き方については、See section コマンドの定義

残りの要素は、関数の本体(body)です。関数の本体とは、関数の 実際の作業を行う Lispコードです(Lisp プログラミングの観点からは、 "評価される Lisp形式のリスト"ともいえます)。関数が返す値は、 本体の最後の要素が返す値です。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.2.2 簡単なラムダ式の例

次のような関数を例にとって考えます。

 
(lambda (a b c) (+ a b c))

この関数を呼び出すには、次のように、式のCARにこの関数を書きます。

 
((lambda (a b c) (+ a b c))
 1 2 3)

この呼出しは、変数aを1に、変数bを2に、変数 cを3に束縛して、ラムダ式の本体を評価します。本体が評価され ると、この三つの数が加算され、結果は6になります。このため、この関 数の呼出しは6を返します。

また、次の例のように、ある関数呼出しの結果をさらに別の関数の引数にするこ ともできます。

 
((lambda (a b c) (+ a b c))
 1 (* 2 3) (- 5 4))

この式は、引数の1(* 2 3)(- 5 4)を左から右に評価し ます。次に、引数の1と6と1にラムダ式が適用され、結果は8になります。

通常は、このように形式のCARとしてラムダ式を書くのは不便です。特殊 形式letを使ってローカル変数を作成し、その変数に値を設定すれば、同じ 結果を得ることができます(see section ローカル変数)。また、letの方が わかりやすく、使うのも簡単です。実際には、ラムダ式は、シンボルの関数定義とし て格納して名前をもつ関数を作成したり、ほかの関数の引数として渡すために使われ ます(see section 匿名関数)。

しかし、特殊形式letが発明されるまでの以前のLispでは、明示 的なラムダ式への呼出しはよく使われていました。当時は、ローカル 変数に値を束縛して初期化するには、この方法を使うしかありませんで した。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.2.3 引数リストの高度な機能

この例の(lambda (a b c) (+ a b c))という簡単な関数では、三つの引数変数を指定しているため、この関数は三つの引数を指定して呼 び出さなければなりません。引数を二つだけしか指定せずに、あるいは、四 つも指定してこの関数を呼び出そうとすると、 wrong-number-of-argumentsエラーが発生します。

一部の引数を省略できる関数を書いた方が便利な場合もよくあります。 たとえば、substring関数は、文字列、開始インデックス、終了 インデックスの三つの引数を受けつけます。しかし、3番目の引数は、省 略すると文字列のlengthになります。また、list関数や +関数のように、関数が不定個の引数を受けつけるようにすると 便利な場合もあります。

関数を呼び出す時に省略できる引数を指定するには、 &optionalキーワードをオプション引数の前につけます。余っ ている0個以上の引数を要素とするリストを指定するには、最後の引数 の前に&restキーワードをつけます。

したがって、引数リストの完全な構文は次のようになります。

 
(required-vars…
 [&optional optional-vars…]
 [&rest rest-var])

角括弧は、&optional節と&rest節、およびそれらの後の変 数が省略可能であることを示しています。

関数を呼び出す時は、required-varsのそれぞれについて実引数 を一つずつ指定しなければなりません。optional-varsには実引数 を指定しなくてもかまいません。また、ラムダ・リストで&rest が使われていなければ、仮引数の個数を超える実引数を指定することは できません。&restが使われている場合は、余分な実引数をいく つでも指定できます。

オプション引数や残余引数に対する実引数を省略した場合のデフォルト値 は、つねにnilです。明示的にnilが引数に指定されたの か、それとも引数が省略されたのかを呼び出された関数から区別するこ とはできません。ただし、呼び出された関数の本体は、nilを別 の意味を示す省略値として扱うこともできます。たとえば、 substringは、3番目の引数にnilが指定されている場合、 指定されている文字列の長さを使います。

Common Lisp注意書き: Common Lispでは、オプション引数が省略され ている場合に使われるデフォルト値を関数に指定できます。Emacs Lisp では、常にnilが使われます。

たとえば、次のような引数リストがあるとします。

 
(a b &optional c d &rest e)

この引数リストは、abを最初の二つの引数に束縛しま す。この二つの引数は必須です。引数がさらに一つまたは二つ指定されて いる場合は、cdはそれぞれ対応する引数に束縛されま す。最初の四つの引数の後の引数はすべて一つのリストにまとめられ、 eはそのリストに束縛されます。引数が二つしか指定されていな い場合は、cnilになります。引数が二つまたは三つの場 合は、dnilになります。引数が四つ以下の場合は、 enilになります。

オプション引数の後に必須引数を置くことはできません (無意味です)。 その理由を考えるため、上記の例のcが省略可能で、dが 必須であると仮定してみます。実引数を三つ与えたとすると、三つめの引 数はどの変数に束縛されるでしょうか?同様に、&rest引数の後 にさらに(必須やオプションの)引数を置くのも無意味です。

引数リストと正しい呼出しの例をいくつか示します。

 
((lambda (n) (1+ n))                ; 必須引数が一つ:
 1)                                 ; 引数は一つだけで、省略不可能
     ⇒ 2
((lambda (n &optional n1)           ; 必須引数が一つ、オプション引数が一つ:
         (if n1 (+ n n1) (1+ n)))   ; 引数は1個または2個
 1 2)
     ⇒ 3
((lambda (n &rest ns)               ; 必須引数が一つ、残余引数が一つ:
         (+ n (apply '+ ns)))       ; 引数は1個以上
 1 2 3 4 5)
     ⇒ 15

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.2.4 関数の説明文字列

ラムダ式のラムダ・リストの直後には、説明文字列(documentation string)を置くこともできます。この文字列は、関数の実行には影響を 与えません。この文字列は一種のコメントですが、Lisp環境内に実際に 存在する系統だったコメントであり、Emacsのヘルプ機能によって利用 できます。documentation-stringのアクセス方法については、 See section ヘルプ

たとえ、自分のプログラムからしか呼び出されない関数でも、プログラ ムのすべての関数には説明文字列を用意しておくのがよいでしょう。 説明文字列はコメントのようなものですが、コメントよりも調べやす いという特徴があります。

aproposは説明文字列の1行目だけを表示します。このため、説明文 字列の1行目はそれだけで完結していなければなりません。説明文字 列の1行目には、関数の用途を要約した一つか二つの文を置いてください。

通常、説明文字列の先頭はソース・ファイル内で字下げしますが、 この字下げのための空白は開き二重引用符の前であるため、説明 文字列には含まれません。テキストがプログラム・ソースと揃うように、 残りの行を字下げする人もいますが、それは間違いです。2 行目以降の字下げは、説明文字列内に含まれます。ソース・コード ではきれいに見えても、ヘルプ・コマンドで表示すると乱れてしまいます。

説明文字列の後には関数の必須要素(本体)があるのに、説明文字列を省 略可能にできるのには理由があります。文字列を評価すると、副作用な しでその文字列自体が返されるため、文字列が関数の本体の最後の要素 でなければ問題はありません。このように、実際には、本体の1番目の 部分と説明文字列が混同される恐れはありません。本体が文字列だけな らば、その文字列は戻り値と説明の両方になります。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.3 関数の命名

ほとんどのコンピュータ言語では、関数にはかならず名前があり、名前 を持たない関数というのは無意味です。Lispでは、厳密な意味での関数 には名前はありません。関数は、最初の要素がlambdaであるリ ストか、プリミティブなsubrオブジェクトです。

しかし、関数の名前としてシンボルを使うこともできます。この場合、 シンボルの関数セル(function cell)(see section シンボルの要素)に関数を置きます。すると、シンボル自体が有効で呼出し可能な関数になり、関数セルが参照するリストまたはsubrオブジェク トに等価になります。関数セルの内容は、シンボルの関数定義 (function definition)とも呼ばれます。シンボルのかわりにシンボル の関数定義を使う方法として、シンボル関数間接参照(symbol function indirection)という方法があります。シンボル関数間接参照を 参照してください。

実際には、ほとんどすべての関数にこのように名前が与えられ、その名 前によって参照されます。たとえば、シンボルcarの関数セルに はプリミティブsubrオブジェクトの#<subr car>が格納されてい るため、シンボルcarは、関数と同 様に機能します。

関数に名前を与えるのは、Lisp式で関数を参照しやすくするためです。 #<subr car>などのプリミティブsubrオブジェクトの場合、名前 でしか参照できません。このようなオブジェクトには、可読の構文はあ りません。Lispで書かれた関数を呼び出す場合も、明示的なラムダ式を 記述するより名前を使ったほうが便利です。また、名前をもつ関数は、 自分自身を参照できます(再帰呼出しが可能です)。関数の定義にその 関数の名前を記述するほうが、関数定義が自分自身を指すようにするよ りも簡単です(関数定義が自分自身を指すようにすることは不可能では ありませんが、実際にはさまざまな短所があります)。

関数を示すのに、その関数の名前であるシンボルを使うことがよくあり ます。たとえば、シンボルcarとその関数定義に格納されている プリミティブsubrオブジェクトを区別せずに、"関数car"と表 現することがよくあります。ほとんどの場合、この二つを区別する必要 はありません。

ただし、関数に一意な名前を与える必要はないことを覚えておいてくだ さい。ある特定の関数オブジェクトは、通常は一つのシンボルの 関数セルだけに格納されていますが、単に便宜上そうなっているだけで す。fsetを使えば、複数のシンボルに一つの関数オブジェクトを 簡単に格納できます。この場合、どのシンボルも、同じ関数の名前とし て同様に通用します。

また、関数名として使われているシンボルを変数として使うこともでき ます。シンボルのこの二つの使い方は独立しており、衝突しません。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.4 関数の定義方法

通常、関数をはじめて作成するときは、関数に名前を与えます。関 数を定義する(defining a function)とは、このことであり、関数を定義 するにはdefun特殊形式を使います。

Special Form: defun name argument-list body-forms

通常、新しいLisp関数を定義するには、defunを使います。 defunは、シンボルnameを次のような関数として定義しま す。

 
(lambda argument-list . body-forms)

defunは、このラムダ式をnameの関数セルに格納します。 返される値はnameですが、通常はこの値を使うことはありません。

以前に説明したように(see section ラムダ式)、 argument-listは引数の名前のリストであり、キーワードの &optional&restが使えます。また、body-forms の最初の二つの部分は、説明文字列とinteractive宣言にすることも できます。

シンボルの値セルと関数セルは独立しているため、同じシンボル nameを変数として使っても衝突は発生しません(See section シンボルの要素)。

例をいくつか示します。

 
(defun foo () 5)
     ⇒ foo
(foo)
     ⇒ 5

(defun bar (a &optional b &rest c)
    (list a b c))
     ⇒ bar
(bar 1 2 3 4 5)
     ⇒ (1 2 (3 4 5))
(bar 1)
     ⇒ (1 nil nil)
(bar)
error--> Wrong number of arguments.

(defun capitalize-backwards ()
  "単語の最後の文字を大文字にします。"
  (interactive)
  (backward-word 1)
  (forward-word 1)
  (backward-char 1)
  (capitalize-word 1))
     ⇒ capitalize-backwards

既存の関数を誤って再定義してしまわないように注意してください。 defunは、carなどのプリミティブ関数でも何の忠告もな しに再定義してしまいます。 Lispでは不注意な再定義と意図的な再定 義を区別できないため、すでに定義されている関数の再定義は慎重に行っ てください。

Function: defalias name definition

この特殊形式は、シンボルnameを、定義definition (任意 の有効なLisp関数が使えます)をもつ関数として定義します。

defaliasは、特定の関数名が定義されている箇所(とくに、ロー ドされるソース・ファイルに関数名が明示的に現れる箇所)で使ってくだ さい。これは、defaliasdefunと同様に関数を定義し ているファイルを記録するためです(see section アンロード)。

これに対して、ほかの目的で関数の定義を操作するプログラムでは、定義 されているファイルを記録しないfsetを使ってください。

defunと同様に、関数を定義し、コードを展開するようにLispコン パイラに指示するdefsubstも参照してください。See section インライン関数


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.5 関数の呼出し

関数の定義は、作業の半分でしかありません。関数は、呼び出す (call) (動作するように指示する)までは何もしません。関数の呼出しは、起動(invocation)ともよばれます。

関数を起動するための最も一般的な方法は、リストを評価することです。 たとえば、(concat "a" "b")というリストを評価することは、 "a""b"を引数として関数concatを呼び出すこ とです。評価の説明については、See section 評価

プログラム中の式としてこのリストを書くと、関数の名前はプログラム の一部になります。つまり、呼び出される関数とその関数に与える引数 を、プログラムを書く時に選択することになります。通常は、これで十 分です。しかし、呼び出される関数を実行時に決定しなければならない 場合もあります。そのためには、funcall関数とapply関 数を使います。

Function: funcall function &rest arguments

funcallは、argumentsを引数としてfunctionを呼 び出し、functionが返した値を返します。

funcallは関数であるため、functionを含めたすべての引 数は、funcallが呼び出される前に評価されます。つまり、任意 の式を使って、呼び出される関数を指定できます。また、 funcallは、argumentsに書かれている式ではなく、その 値だけを使います。これらの値は、functionが呼び出されるとき、 再び評価されることはありませんfuncallは、引数が 評価された時点で、関数を呼び出すための通常の手順に入ります。

function引数は、Lispの関数か、プリミティブ関数でなければな りません。特殊形式とマクロは"評価されていない"引数式が与えられ た場合だけ意味をもつため、ここでは使えません。上記のように、 funcallは未評価の引数を知ることができないためです。

 
(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の例を比較してください。

Function: apply function &rest arguments

applyは、funcallと同様にargumentsを引数とし てfunctionを呼び出しますが、一つだけ違いがあります。 argumentsの最後の要素が、単一の引数ではなく、 functionに渡される引数のリストになっています。また、 applyは、このリストの各要素がそれぞれ一つの引数になるよう に、リストを散開(spread)します。

applyは、functionを呼び出した結果を返します。 applyでも、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のおもしろい使い方に関する例は、マッピング関数mapcarの説明を参照してください。

Lisp関数の場合、(フック変数や属性リストのように)引数とし て関数を受け取ったり、データ構造内に関数を置き、funcallま たはapplyを使ってその関数を呼び出すことがよくあります。関 数引数を受けつける関数は、ファンクショナル(functional)とも呼ば れます。

ファンクショナルを呼び出す場合、何もしない関数を引数として指定でき ると便利な場合があります。何もしない関数を二つ紹介します。

Function: identity arg

この関数はargを返すだけで、副作用はありません。

Function: ignore &rest args

この関数は、引数を無視してnilを返します。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.6 マッピング関数

マッピング関数(mapping function)は、ある関数をリストなどの各要素に適用します。Emacs Lispには、マッピング関数が三つあります。ここで 説明するmapcarmapconcatは、リストをスキャンしま す。もう一つのマッピング関数であるmapatomsについては、 シンボルの作成と internを参照してください。

Function: mapcar function sequence

mapcarは、sequenceの各要素にfunctionを順番に 適用し、その結果のリストを返します。

sequence引数には、リスト、ベクタ、または文字列を指定できま す。結果は常にリストになります。結果のリストの要素数は、 sequenceの 要素数と同じです。

 
例:

(mapcar 'car '((a b) (c d) (e f)))
     ⇒ (a c e)
(mapcar '1+ [1 2 3])
     ⇒ (2 3 4)
(mapcar 'char-to-string "abc")
     ⇒ ("a" "b" "c")

;; my-hooksに含まれる各関数を呼び出します。
(mapcar 'funcall my-hooks)

(defun mapcar* (f &rest args)
  "すべての引数のcarに関数を順番に適用し、
結果のリストを返します。"
  ;; リストの要素がまだ残っていれば、
  (if (not (memq 'nil args))              
      ;; それぞれのCARに関数を適用します。
      (cons (apply f (mapcar 'car args))  
            (apply 'mapcar* f             
                   ;; 残りの要素について再帰実行します。
                   (mapcar 'cdr args)))))

(mapcar* 'cons '(a b c) '(1 2 3 4))
     ⇒ ((a . 1) (b . 2) (c . 3))
Function: mapconcat function sequence separator

mapconcatは、sequenceの各要素にfunctionを適用 します。functionが返す値は文字列でなければならず、結果は連 結されます。mapconcatは、各要素について返された文字列の間 に、文字列separatorを挿入します。通常、separatorには、 スペース、カンマほかの適当な句読点を指定します。

function引数は、引数を一つ取り、文字列を一つ返す関数でなけれ ばなりません。

 
(mapconcat 'symbol-name
           '(The cat in the hat)
           " ")
     ⇒ "The cat in the hat"

(mapconcat (function (lambda (x) (format "%c" (1+ x))))
           "HAL-8000"
           "")
     ⇒ "IBM.9111"

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.7 匿名関数

Lispでは、関数は、lambdaで始まるリストか、そのようなリス トからコンパイルされたバイトコード関数か、プリミティブなsubrオブ ジェクトです。名前は"おまけ"です。通常、関数はdefunを使っ て定義し、同時に名前も与えますが、明示的にラムダ式(匿名関数)を使っ たほうがわかりやすい場合もあります。ラムダ式であるリストは、間数 名が使えるところならばどこでも使えます。

ラムダ式リストは、どのような手段で作成しても有効な関数になります。 たとえば、次のような例も有効です。

 
(setq silly (append '(lambda (x)) (list (list '+ (* 3 4) 'x))))
⇒ (lambda (x) (+ 12 x))

上記の例は、(lambda (x) (+ 12 x))のようなリストを評価し、 それをsillyの(関数定義ではなく)値にします。

この関数を呼び出す方法を次に示します。

 
(funcall silly 1)
⇒ 13

(sillysilly関数定義ではないため、 (silly 1)のように書いても動作しませんsillyには関数定義は設定されておらず、変数としての値が与え られているだけです。)

ほとんどの場合、匿名関数は、プログラム中に固定的に書かれます。た とえば、リストの各要素に関数を適用するmapcar関数に匿名関 数を引数として渡すとします。数値を2倍する匿名関数を渡す例を次に 示します。

 
(defun double-each (list)
  (mapcar '(lambda (x) (* 2 x)) list))
⇒ double-each
(double-each '(2 11))
⇒ (4 22)

このような場合、通常は、単なるクォートを使って匿名関数をクォート せずに、特殊形式functionを使います。

Special Form: function function-object

この特殊形式は、function-objectを評価せずに返します。上記の 例では、quoteと等価です。しかし、この特殊形式は. function-objectが関数としてだけ使われることをEmacs Lispコ ンパイラに伝えるため、コンパイルが安全に行なわれるようになります。 quoteするquoteと比較してみてください。

quoteの代わりにfunctionを使うと、 コンパイルしようとする関数またはマクロの内部で違いがあります。 例を示しましょう。

 
(defun double-each (list)
  (mapcar (function (lambda (x) (* 2 x))) list))
⇒ double-each
(double-each '(2 11))
⇒ (4 22)

このdouble-eachの定義をコンパイルすると、匿名関数もコンパ イルされます。一方、通常のquoteを使った前の例の場合、 mapcarに渡される引数は、次に示すリストになります。

 
(lambda (x) (* x 2))

このリストは関数の形をしていますが、Lispコンパイラは、 mapcarがリストに対して行う処理を知らないため、このリスト が関数であることを仮定できません。第3要素のCARがシンボル *であることをmapcarが調べる可能性もあるためです。 functionの利点は、変化しない関数をコンパイルしても問題が ないことをコンパイラに伝えられることにあります。

関数の名前をクォートするときに、quoteではなく functionと書く場合もありますが、これは単にわかりやすくす るためだけです。

 
(function symbol) ≡ (quote symbol) ≡ 'symbol

functionと匿名関数を使った実例については、説明文字列へのアクセスdocumentationを参照してください。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.8 関数セルの内容へのアクセス

シンボルの関数定義(function definition)は、シンボルの関数 セルに格納されているオブジェクトです。ここでは、シンボルの関数セ ルをアクセス、テスト、設定する関数について説明します。

シンボル関数間接参照indirect-function関数も参照し てください。

Function: symbol-function symbol

この関数は、symbolの関数セルに格納されているオブジェクトを 返します。シンボルの関数セルがvoidならば、void-functionエラー が発生します。

この関数は、返されるオブジェクトが有効な関数かどうかはチェックし ません。

 
(defun bar (n) (+ n 2))
     ⇒ bar
(symbol-function 'bar)
     ⇒ (lambda (n) (+ n 2))
(fset 'baz 'bar)
     ⇒ bar
(symbol-function 'baz)
     ⇒ bar

シンボルに関数定義をまったく与えていない場合、そのシンボルの関数セル がvoidであるといいます。つまり、関数セルにはLisp オブジェクトが入っていない状態です。このようなシンボルを呼び出そ うとすると、void-functionエラーが発生します。

voidは、nilやシンボルvoidとは異なります。シンボル のnilvoidはLispオブジェクトであり、ほかのオブジェ クトと同様、関数セルに格納できます(さらに、defunを使って 定義すれば有効な関数になります)。voidな関数セルには、オブジェ クトがまったく入っていません。

シンボルの関数定義がvoidかどうかを調べるには、fboundpを使います。 いったん関数定義を与えたシンボルをもう一度voidにするには、fmakunboundを 使います。

Function: fboundp symbol

この関数は、シンボルの関数セルにオブジェクトがあればtを返 し、そうでなければnilを返します。オブジェクトが有効な関数 かどうかはチェックしません。

Function: fmakunbound symbol

この関数は、symbolの関数セルをvoidにします。この関数を呼び 出した後、関数セルにアクセスしようとすると、void-function エラーが発生します(ローカル変数makunboundを 参照してください)。

 
(defun foo (x) x)
     ⇒ x
(foo 1)
     ⇒1
(fmakunbound 'foo)
     ⇒ x
(foo 1)
error--> Symbol's function definition is void: foo
Function: fset symbol object

この関数は、symbolの関数セルにobjectを格納し、 objectを返します。通常、objectは関数または関数名です が、チェックは行なわれません。

この関数には、主に三つの使い方があります:

最初の二つの場合の例を示します:

 
;; carと同じ定義をfirstに与えます。
(fset 'first (symbol-function 'car))
     ⇒ #<subr car>
(first '(1 2 3))
     ⇒ 1

;; シンボルcarxfirstの関数定義にします。
(fset 'xfirst 'car)
     ⇒ car
(xfirst '(1 2 3))
     ⇒ 1
(symbol-function 'xfirst)
     ⇒ car
(symbol-function (symbol-function 'xfirst))
     ⇒ #<subr car>

;; 名前つきのキーボード・マクロを定義します。
(fset 'kill-two-lines "\^u2\^k")
     ⇒ "\^u2\^k"

関数の定義方法defalias関数も参照してください。

すでに定義されている関数を拡張する関数を作成する場合、次に示す常 套手段がときどき使われます。

 
(fset 'old-foo (symbol-function 'foo))
(defun foo ()
  "more-soを除いてold-fooと同じ。"
  (old-foo)
  (more-so))

fooが自動ロードされるように定義されている場合、この方法は うまくいきません。そのような場合、fooold-fooを呼 び出すと、Lispシステムはファイルをロードしてold-fooを定義 しようとします。ファイルをロードすると、おそらくold-fooで はなくfooが定義されてしまい、適切な結果が得られません。こ の問題を避けるには、ファイルをロードしてからfooの以前の定 義を置き換えるしかありません。

しかし、ほかの箇所で定義されている関数をLispファイルで再定義するの は、どちらにしろモジュール化に反し、きれいではありません。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.9 インライン関数

インライン関数(inline function)を定義するには、 defunではなくdefsubstを使います。インライン関数 は普通の関数と同様に動作します。ただし、インライン関数への呼出しを コンパイルすると、関数の定義が呼び出し側に展開されます。

関数をインラインにすると、明示的な呼出しが高速に実行されるよう になります。しかし、欠点もあります。まず、柔軟性が低下します。関 数の定義を変更しても、すでにインライン展開されている呼出しには 再コンパイルするまで古い定義が使われます。関数の再定義の柔軟性は Emacsの重要な特徴であるため、実行速度が本当に重要でなければ、関 数をインラインにしないでください。

もう一つの欠点は、大きな関数をインラインにすると、コンパイルされ たコードのファイル・サイズとメモリ・サイズが増加することです。イ ンライン関数の速度面での利点は小さな関数の場合にもっとも発揮され るので、通常は大きな関数をインラインにしないでください。

インライン関数が実行するのと同じコードに展開されるマクロを定義す ることもできます。しかし、マクロには限界があります。マクロは明示 的にしか使うことができず、applymapcarなどで呼び 出すことはできません。また、通常の関数をマクロに書き換えるには手 間がかかります(See section マクロ)。通常の関数をインライン関数に書き 換えるのは非常に簡単で、defundefsubstで置き換え るだけです。インライン関数に書く引数は一度だけ評価されるため、マ クロのように本体内での引数の使用回数を気にする必要はありません (See section マクロ引数の反復的評価)。

インライン関数を使うと、マクロと同様に、その定義にしたがって同じ ファイル内のその関数の呼出しが展開されます。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.10 関数に関するほかの項目

関数の呼出しと定義に関する機能をもつ関数をいくつか次の表に示し ます。これらの関数は別のところでも触れられていますが、クロスリファ レンスを示しておきます。

apply

関数の呼出しを参照。

autoload

自動ロードを参照。

call-interactively

対話的呼出しを参照。

commandp

対話的呼出しを参照。

documentation

説明文字列へのアクセスを参照。

eval

Evalを参照。

funcall

関数の呼出しを参照。

ignore

関数の呼出しを参照。

indirect-function

シンボル関数間接参照を参照。

interactive

interactiveを使うを参照。

interactive-p

対話的呼出しを参照。

mapatoms

シンボルの作成と internを参照。

mapcar

マッピング関数を参照。

mapconcat

マッピング関数を参照。

undefined

キー検索を参照。


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Yasutaka SHINDOH on September, 29 2006 using texi2html 1.76.