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

11. 関数

Lispプログラムは、主にLisp関数から構成されます。 本章では、関数とはなにか、引数をどのように受け取るのか、 どのように関数を定義するのかを説明します。

11.1 関数とはなにか  Lisp functions vs. primitives; terminology.
11.2 ラムダ式  How functions are expressed as Lisp objects.
11.3 関数を命名する  A symbol can serve as the name of a function.
11.4 関数を定義する  Lisp expressions for defining functions.
11.5 関数呼び出し  How to use an existing function.
11.6 マップ関数  Applying a function to each element of a list, etc.
11.7 無名関数  Lambda expressions are functions with no names.
11.8 関数セルの内容の参照  Accessing or setting the function definition of a symbol.
11.9 インライン関数  Defining functions that the compiler will open code.
11.10 関数に関連したその他の話題  Cross-references to specific Lisp primitives that have a special bearing on how functions work.


11.1 関数とはなにか

一般的には、関数とは、引数(arguments)と呼ばれる値を与えられ、 計算を行うための規則です。 この計算結果を関数の値と呼びます。 計算では副作用、つまり、変数の値やデータ構造の内容に継続する変更 を伴うこともできます。

Emacs Lispの関数や関数のようなオブジェクトに関する重要な用語をあげておきます。

関数
Emacs Lispでは、Lispプログラムにおいて引数に適用可能ものは なんであれ関数(function)である。 Lispで書いた関数を意味する場合もある。 スペシャルフォームやマクロは関数ではない。

基本関数
基本関数(primitive)は、carappendなどのCで書いた Lispから呼び出し可能な関数である。 これらの関数は、組み込み関数とかsubrsとも呼ぶ。 (スペシャルフォームは基本関数とも考えられる。)

関数を基本関数として実装する理由は、 それが基本的なものである、 それがオペレーティングシステムの機能に対する 低レベルのインターフェイスを提供する、 あるいは、高速に動作する必要があるからである。 基本関数を変更したり追加する唯一の方法は、 Cソースを変更してエディタを再コンパイルすることである。 see section B.5 Emacs基本関数の書き方

ラムダ式
ラムダ式(lambda expression)は、Lispで書いた関数である。 これらについては以下の節で説明する。 See section 11.2 ラムダ式

スペシャルフォーム
スペシャルフォーム(special form)は関数に似た基本関数であるが、 その引数すべてを普通のようには評価しない。 引数の一部を評価したり、普通とは異なる順序で評価したり、 複数回評価したりする。 多くのスペシャルフォームについては、 9. 制御構造で説明してある。

マクロ
マクロ(macro)は、プログラマがLispで定義した構文である。 マクロと関数との違いは、マクロは、 読者が書いたLisp式をもとの式のかわりに評価される等価な式に変換する。 マクロは、スペシャルフォームでできる種類のことを Lispプログラマに提供する。 マクロの定義方法と使い方については、see section 12. マクロ

コマンド
コマンド(command)とは、 command-executeが起動できるオブジェクトであり、 キー列に対して定義できる。 いくつかの関数はコマンドである。 Lispで書いた関数に対話宣言(see section 20.2 コマンドの定義)が含まれているとき、 その関数はコマンドである。 そのような関数は、他の関数と同様にLisp式から呼び出すことができる。 その場合、関数がコマンドであるという事実は関係ない。

キーボードマクロ(文字列かベクトル)もコマンドであるが、 それらは関数ではない。 シンボルの関数定義がコマンドであれば、シンボルはコマンドである。 そのようなシンボルは、M-xで起動できる。 シンボルの定義が関数であれば、シンボルは関数でもある。

打鍵コマンド
打鍵コマンド(keystroke command)とは、 キー列(典型的には1から3打鍵)にバインドされたコマンドである。 ここでの区別は、Emacs以外のエディタの『コマンド』の意味との 混乱を防ぐためであるが、 Lispプログラムにとっては、この区別は普通は重要ではない。

バイトコード関数
バイトコード関数(byte-code function)とは、 バイトコンパイラでコンパイルした関数である。 see section 2.3.15 バイトコード関数型

Function: functionp object
この関数は、objectが、なんらかの関数、スペシャルフォーム、 マクロであれば、tを返す。

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


11.2 ラムダ式

Lispで書いた関数はつぎのようなリストです。

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

このようなリストをラムダ式(lambda expression)と呼びます。 Emacs Lispでは、これは式として正しいもので、 それ自身に評価されます。 Lispの他の方言では、ラムダ式は正しい式ではありません。 いずれの場合でも、その主な用途は式として評価することではなく、 関数として呼び出すことです。

11.2.1 ラムダ式の構成要素  The parts of a lambda expression.
11.2.2 簡単なラムダ式の例  A simple example.
11.2.3 引数リストのその他の機能  Details and special features of argument lists.
11.2.4 関数の説明文字列  How to put documentation in a function.


11.2.1 ラムダ式の構成要素

Lispで書いた関数(『ラムダ式』)はつぎのようなリストです。

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

ラムダ式の先頭要素は、つねにシンボルlambdaです。 このシンボルは、リストが関数を表すことを示します。 関数はlambdaで始まると定義してあるのは、 他の目的向けの他のリストが誤って正しい関数とならないようにするためです。

第2要素は、シンボルのリスト、つまり、引数変数名です。 これをラムダリスト(lambda list)と呼びます。 Lisp関数が呼ばれると、引数値をラムダリストの変数に対応させ、 指定した値を持つローカル束縛になります。 See section 10.3 ローカル変数

説明文字列は、関数定義の内側にあるLisp文字列オブジェクトであり、 Emacsのヘルプ機能に対して関数を記述します。 See section 11.2.4 関数の説明文字列

対話宣言は、(interactive code-string)の形式のリストです。 この宣言は、関数が対話的に使われたときに、 どのように引数を与えるかを指定します。 この宣言を有する関数をコマンド(commands)と呼びます。 コマンドは、M-xで呼び出したり、キーにバインドできます。 このように呼ばれることを意図していない関数には、 対話宣言を付けてはいけません。 対話宣言の書き方については、See section 20.2 コマンドの定義

残りの要素は、関数の本体(body)です。 関数の動作を行うLispコードです (Lispプログラマとしては、『評価するべきLispフォームのリスト』という)。 関数が返す値は、本体の最後の要素が返す値です。


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 10.3 ローカル変数)を使って、 ローカル変数を作ってそれらに値を与えても、同じ結果を得られます。 さらに、letは見通しがよく使いやすいです。 実用上、ラムダ式は、シンボルの関数定義として格納して名前付き関数を作るか、 他の関数に引数として渡します(see section 11.7 無名関数)。

しかしながら、スペシャルフォームletがなかった初期のLispでは、 ラムダ式を明示的に呼び出すことはとても便利でした。 その頃では、ラムダ式はローカル変数を束縛し初期化する唯一の方法でした。


11.2.3 引数リストのその他の機能

単純な関数の例(lambda (a b c) (+ a b c))では、 3つの引数変数を指定しているので、これは3引数で呼び出す必要があります。 2引数や4引数で呼び出そうとすると、 エラーwrong-number-of-argumentsになります。

特定の引数を省略できる関数を書けると便利なことがしばしばあります。 たとえば、関数substringは3つの引数、つまり、 文字列、開始と終了の添字を取りますが、 第3引数を省略するとデフォルトは文字列のlengthになります。 list+のように、 特定の関数では任意個数の引数を受け付けると便利なこともあります。

関数呼び出し時に省略してもよい引数を指定するには、 省略可能な引数のまえにキーワード&optionalを含めるだけです。 0個以上の引数のリストを指定するには、 最後の引数のまえにキーワード&restを含めます。

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

 
(required-vars...
               ; 必須の引数
 [&optional optional-vars...]
               ; 省略可能な引数
 [&rest rest-var])
               ; 残りの引数

角括弧は、&optional&restの節や それに続く変数は省略できることを示します。

関数呼び出し時には、各required-varsに1つの実引数が必要です。 0個以上のoptional-varsにも実引数が必要ですが、 ラムダリストに&restがない限り、 optional-varsの個数を超える実引数は指定できません。 &restがあれば、任意個の余分な実引数を指定できます。

&optional&restに対応する実引数を省略すると、 それらのデフォルトはnilです。 関数では、nilを明示した引数と省略した引数とを区別する方法はありません。 しかしながら、関数本体でnilを適切な意味ある値の省略と みなすことは自由です。 substringはそのようにしています。 substringの第3引数がnilであると、 指定した文字列の長さを使うことを意味します。

Common Lispに関した注意: Common Lispでは、省略可能引数を省略したときのデフォルト値を関数で指定できる。 Emacs Lispではつねにnilを使う。 Emacs Lispには、明示的に引数を指定したかどうか調べる 『supplied-p』変数はない。

たとえば、引数リストはつぎのようになります。

 
(a b &optional c d &rest e)

これは、abに最初の2つの実引数を束縛し、これらは必須です。 さらに1個か2個の引数を指定すると、 それらは、それぞれcdに束縛します。 最初の4個よりあとの引数はリストにまとめ、 eにそのリストを束縛します。 引数が2個だけであると、cnilです。 引数が2個か3個だけであると、dnilです。 引数が4個以下であると、enilです。

省略可能な引数のあとに必須引数を指定する方法はありませんし、 それには意味がありません。 なぜそうなのかを理解するために、上の例で、 cは省略可能であり、dは必須であるとしましょう。 3つの実引数を指定したとき、どの引数を3番目と考えるのでしょう? 同様に、&restのうしろに余分に(必須、もしくは省略可能な)引数が あっても意味がありません。

引数リストと正しい呼び出しの例をあげます。

 
((lambda (n) (1+ n))                ; 1個が必須
 1)                                 ; 引数は1個だけ
     => 2
((lambda (n &optional n1)           ; 1個は必須、1個は省略可
         (if n1 (+ n n1) (1+ n)))   ; 引数は1個か2個
 1 2)
     => 3
((lambda (n &rest ns)               ; 1個は必須、あとは残り全部
         (+ n (apply '+ ns)))       ; 引数は1個以上いくつでもよい
 1 2 3 4 5)
     => 15


11.2.4 関数の説明文字列

ラムダ式には、ラムダリストの直後に 説明文字列(documentation string)があってもかまいません。 この文字列は関数の実行には影響しません。 コメントのようなものですが、Lisp内部に現れる系統的なコメントであり、 Emacsのヘルプ機能が使用します。 documentation-stringの参照方法については、See section 23. 説明文

読者のプログラムの関数すべてに、 たとえ内部的に使用されるものであっても説明文字列を与えることはよいことです。 説明文字列はコメントに似ていますが、参照するのはもっと簡単です。

説明文字列の先頭行は、その1行で完結しているべきです。 というのは、aproposは先頭行だけを表示するからです。 関数の機能をまとめた1つか2つの文にしましょう。

説明文字列の先頭は、ソースファイル上では普通字下げしてあるでしょうが、 それらの空白は文字列を始めるダブルクォートのまえにありますから、 それらは文字列の一部ではありません。 説明文字列の残りの行を字下げして、 プログラムソース上でテキスト行が揃うようにする人もいます。 しかし、それはまちがいです。 後続の行の字下げは文字列の内側にあります。 ソースファイルで綺麗に見えても、 ヘルプコマンドの表示では不恰好になります。

関数の必須の構成要素(本体)があとに続くのに、 説明文字列を省略できるのを不思議に思うかもしれません。 文字列を評価すると、副作用なしに、その文字列を返すので、 それが本体の最後のフォームでなければ、なんの効果もありません。 したがって、実用上、本体の最初のフォームと 説明文字列を混同することはありません。 本体のフォームが文字列だけであると、 それは戻り値でもあり説明文字列でもあります。


11.3 関数を命名する

ほとんどの計算機言語では、各関数には名前があります。 名前のない関数という考えは本質的ではありません。 Lispでは、もっとも厳密にいえば、関数には名前はありません。 関数は、先頭要素が単にlambdaであるリスト、 バイトコード関数オブジェクト、あるいは、基本関数のsubrオブジェクトです。

しかしながら、シンボルは関数の名前として働きます。 シンボルの関数セル(function cell、see section 7.1 シンボルの構成要素)に 関数を入れると、このようになります。 そうすると、シンボルそのものは正当な呼び出し可能な関数となり、 関数セルが参照するリストやsubrオブジェクトと等価になります。 関数セルの内容をシンボルの関数定義(function definition)とも呼びます。 シンボルのかわりにシンボルの関数定義を使う処理を シンボルの関数間接(symbol function indirection)と呼びます。 See section 8.2.4 シンボルの関数間接

実用上、ほとんどすべての関数には、このようにして名前が付いていて、 その名前で参照します。 たとえば、シンボルcarは、 その関数セルに基本関数のsubrオブジェクト#<subr car>が格納してあるので、 その動作を行う関数として動作します。

関数に名前を与えるのは、Lisp式からその名前で参照できると便利だからです。 #<subr car>のような基本関数のsubrオブジェクトでは、 名前はそれらを参照する唯一の方法です。 そのようなオブジェクトには入力構文はありません。 Lispで書いた関数では、明示的なラムダ式より名前を使うほうがより便利です。 また、関数に名前があればそれを参照できます。 つまり、再帰呼び出しができます。 関数の名前をその定義そのものに書くことは、 関数定義がそれ自身を指すようにする (これは不可能ではないにしても、実用上はさまざまな欠点がある)よりは、 とても便利です。

関数を指名するシンボルで関数をしばしば識別します。 たとえば、しばしば『関数car』といって、 シンボルcarと関数定義である基本関数のsubrオブジェクトとを区別しません。 ほとんどの目的には、区別する必要はありません。

たとえそうであっても、関数に一意な名前は必要ないことを 心に留めておいてください。 関数オブジェクトは普通1つのシンボルの関数セルだけに現れますが、 これは単なる便法です。 fsetを使って、複数のシンボルに格納するのは簡単です。 そうすると、各シンボルは同じ関数を同等に指名します。

関数名として使うシンボルは、変数としても使えます。 シンボルのこれら2つの使い方は独立していて衝突しません。 (SchemeなどのLispの方言のなかには、 シンボルの値とその関数定義を区別しないものもある。 変数としてのシンボルの値は、その関数定義でもある。) シンボルに関数定義を与えていないと、そのシンボルを関数としては使えません。 これは、シンボルに変数としての値があるかどうかには関係しません。


11.4 関数を定義する

関数を作成するときには、普通、関数に名前を与えます。 これを関数を定義すると呼び、 スペシャルフォームdefunで行います。

Special Form: defun name argument-list body-forms
defunは、新たにLisp関数を定義する普通の方法である。 これは、シンボルnameをつぎのような関数として定義する。

 
(lambda argument-list . body-forms)

defunは、このラムダ式をnameの関数セルに格納する。 値nameを返すが、普通、これは無視する。

前述(see section 11.2 ラムダ式)のように、 argument-listは引数名のリストであり、 キーワード&optional&restが入っていてもよい。 また、body-formsの最初の2つは、説明文字列と対話宣言でもよい。

同一のシンボルnameを変数として使っていても衝突はない。 というのは、シンボルの値セルは関数セルとは独立だからである。 see section 7.1 シンボルの構成要素

例を示そう。

 
(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 ()
  "Upcase the last letter of a word."
  (interactive)
  (backward-word 1)
  (forward-word 1)
  (backward-char 1)
  (capitalize-word 1))
     => capitalize-backwards

既存の関数を意図せずに再定義しないように注意すること。 defunは、たとえcarなどの基本関数であっても、 なんの躊躇も注意もせずに再定義してしまう。 既存関数の再定義は注意深く行うが、 不本意な再定義と熟考した再定義を区別する方法はない。

Function: defalias name definition
このスペシャルフォームは、 シンボルnameを定義definition(任意の正しいLisp関数)とする 関数として定義する。

defaliasを使う正しい場所は、 特定の関数名が定義されている場所である。 特に、ロード中のソースファイルで明示的に名前が現れている場所である。 というのは、defaliasは、defunと同様に、 関数が定義されたファイルを記録するからである(see section 14.7 アンロード)。

一方、他の目的で関数定義を操作するプログラムでは、 そのような記録を保持しないfsetを使うのがよい。

defunのように関数を定義し、かつ、 Lispコンパイラに関数定義を展開するように指示する defsubstも参照してください。 See section 11.9 インライン関数


11.5 関数呼び出し

関数を定義することは、全体の半分でしかありません。 関数を呼ぶまでは、つまり、実行を命じなければ、関数はなにもしません。 関数呼び出しは起動(invocation)ともいいます。

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

読者のプログラムで式としてリストを書くときには、 呼び出す関数名を読者のプログラムに書きます。 つまり、プログラムを書くときに、 どの関数をどれだけの引数で呼び出すかを指定できることを意味します。 これが、普通にしたいことでしょう。 呼び出す関数を実行時に計算する必要がある場合もあるでしょう。 それには、関数funcallを使います。 渡す引数の個数を実行時に決定する必要があるときには、 applyを使います。

Function: funcall function &rest arguments
funcallは、functionargumentsで呼び出し、 functionがなにを返そうともそれを返す。

funcallは関数なので、functionの呼び出しを評価するまえに functionを含めた引数すべてを評価する。 つまり、呼び出す関数を得るためのどんな式でも使えることを意味する。 また、funcallは、読者がargumentsに書いた式を見ることはなく、 それらの値だけを見ることになる。 これらの値は、functionを呼び出す操作において、 2回目の評価を行うことはない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のように、 functionargumentsで呼び出すが、1点だけ異なる。 argumentsの最後はオブジェクトのリストであり、 functionにはこれを、単一のリストではなく、個々の引数として渡す。 これを、applyは、 このリストの個々の要素が引数となるように分配するという。

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を使った興味深い例として、 11.6 マップ関数mapcarの説明を見てほしい。

Lisp関数にとっては、引数として関数を受け取ったり、 データ構造(特に、フック変数や属性リスト)内の関数を探して funcallapplyを使ってそれを呼び出すことは一般的です。 関数引数を受け付ける関数を しばしばファンクショナル(functionals)と呼びます。

場合によっては、ファンクショナルを呼び出すときには、 引数としてなにもしない関数(no-op)を指定できると有用です。 つぎのものは、2種類のなにもしない関数です。

Function: identity arg
この関数はargを返し、副作用を持たない。

Function: ignore &rest args
この関数は引数を無視し、nilを返す。


11.6 マップ関数

マップ関数(mapping function)は、 リストや他の集まりの各要素に指定した関数を適用します。 Emacs Lispにはそのような関数がいくつかあります。 mapcarmapconcatはリストを走査するもので、ここで説明します。 オブジェクト配列obarray内のシンボルについて マップする関数mapatomsについては、 See section 7.3 シンボルの作成とインターン

これらのマップ関数では、文字テーブルは扱えません。 というのは、文字テーブルは疎な配列であり、その添字範囲も非常に大きいからです。 文字テーブルの疎な性質を考慮して文字テーブルについてマップするには、 関数map-char-table(see section 6.6 文字テーブル)を使います。

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* (function &rest args)
  "Apply FUNCTION to successive cars of all ARGS.
Return the list of results."
  ;; リストをつくしていなければ
  (if (not (memq 'nil args))              
      ;; CARに関数を適用する
      (cons (apply function (mapcar 'car args))  
            (apply 'mapcar* function             
                   ;; Recurse for rest of elements.
                   (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を適用する。 それらの結果は、文字列である必要があり、連結される。 mapconcatは、結果の文字列のあいだに文字列separatorを挿入する。 普通、separatorは、空白やコンマ、その他の句読点を含む。

引数functionは、引数を1つ取る関数であり、 文字列を返す必要がある。 引数sequenceは、文字テーブル以外の任意の種類のシーケンスでよい。 つまり、リスト、ベクトル、ブールベクトル、あるいは、文字列である。

 
(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"


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

(silly 1)と書いても動作しない。 なぜなら、この関数は、silly関数定義ではないからである。 sillyには関数定義を与えてなく、 変数としての値を与えただけである。)

ほとんどの場合、無名関数は読者のプログラムに現れる定数です。 たとえば、関数mapcarの引数の1つに渡したいときなどです。 mapcarは、リストの各要素に指定した関数を適用します。

第3引数に関数を取る関数change-propertyを定義します。

 
(defun change-property (symbol prop function)
  (let ((value (get symbol prop)))
    (put symbol prop (funcall function value))))

ここで、数を2倍する関数を渡してchange-propertyを使う 関数を定義します。

 
(defun double-property (symbol prop)
  (change-property symbol prop '(lambda (x) (* 2 x))))

このような場合、つぎのように、無名関数をクォートするには、 単純なクォートのかわりにスペシャルフォームfunctionを使います。

 
(defun double-property (symbol prop)
  (change-property symbol prop
                   (function (lambda (x) (* 2 x)))))

quoteのかわりにfunctionを使った場合に違いがでるのは、 関数double-propertyをコンパイルしたときです。 たとえば、double-propertyの2番目の定義をコンパイルすると、 無名関数もコンパイルされます。 一方、普通のquoteを使った最初の定義をコンパイルすると、 change-propertyへ渡す引数は、書いたとおりのリストです。

 
(lambda (x) (* x 2))

Lispコンパイラは、このリストが関数に見えたとしても、 このリストを関数とはみなしません。 というのは、コンパイラにはchange-propertyがリストになにを行うか わからないからです。 たぶん、第3要素のCARがシンボル*か どうか調べればよいのでしょう! functionを使うと、コンパイラに対して先へ進んで 定数の関数をコンパイルしても安全であることを伝えます。

関数名をクォートするときにquoteのかわりにfunctionを 書くこともありますが、この用法はコメントのようなものです。

 
(function symbol) == (quote symbol) == 'symbol

入力構文#'は、functionの省略形です。 たとえば、

 
#'(lambda (x) (* x x))

は、つぎと等価です。

 
(function (lambda (x) (* x x)))

Special Form: function function-object
このスペシャルフォームは、function-objectを評価せずに function-objectを返す。 この意味ではquoteに等価である。 しかし、これは、Emacs Lispコンパイラに対しては注意書きとして働き、 function-objectを関数としてのみ使う意図があり、 したがって、コンパイルしても安全であることを意味する。 8.3 クォートquoteと比較してほしい。

functionと無名関数を用いた実際的な例は、 23.2 説明文字列の参照documentationを参照してください。


11.8 関数セルの内容の参照

シンボルの関数定義(function definition)とは、 シンボルの関数セルに格納されたオブジェクトです。 ここで説明する関数は、シンボルの関数セルを参照したり、調べたり、 設定したりします。

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

Function: symbol-function symbol
これは、symbolの関数セルのオブジェクトを返す。 シンボルの関数セルが空であると、エラー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で定義しておけば、正しい関数である)。 空の関数セルには、どんなオブジェクトも含まれていません。

シンボルの関数定義が空かどうかはfboundpで調べることができます。 シンボルに関数定義を与えたあとでも、 fmakunboundを使ってふたたび空にできます。

Function: fboundp symbol
この関数は、シンボルの関数セルにオブジェクトが入っていればtを返し、 さもなければnilを返す。 オブジェクトが正しい関数であるかどうか検査しない。

Function: fmakunbound symbol
この関数はsymbolの関数セルを空にする。 これ以降にこのセルを参照しようとすると、 エラーvoid-functionを引き起こす。 (10.4 変数が『空』であるときmakunboundも参照)。

 
(defun foo (x) x)
     => foo
(foo 1)
     =>1
(fmakunbound 'foo)
     => foo
(foo 1)
error--> Symbol's function definition is void: foo

Function: fset symbol definition
この関数は、symbolの関数セルにdefinitionを格納する。 結果はdefinitionである。 通常、definitionは関数か関数名であるべきだが、 そうであるかどうか検査しない。 引数symbolは通常どおり評価される引数である。

この関数の普通の3つの使い方はつぎのとおり。

これらの使用例を示す。

 
;; fooの定義をold-fooに保存する
(fset 'old-foo (symbol-function 'foo))

;; シンボルcarxfirstの関数定義にする
;; (これには、fsetよりdefaliasのほうがよい)
(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"

;; 他の関数を変更する関数
(defun copy-function-definition (new old)
  "Define NEW with the same function definition as OLD."
  (fset new (symbol-function old)))

既存の関数定義を拡張する関数を書くときには、 つぎのような常套句を使うこともあります。

 
(fset 'old-foo (symbol-function 'foo))
(defun foo ()
  "Just like old-foo, except more so."
  (old-foo)
  (more-so))

fooが自動ロードと定義されていると、これは正しく動作しません。 そのような場合には、fooold-fooを呼び出すと、 Lispはファイルをロードしてold-fooを定義しようとします。 しかし、これはold-fooではなくfooを定義するので、 正しい結果を得られません。 この問題を回避する唯一の方法は、 fooの古い定義を移すまえに、確実にファイルをロードしておくことです。

しかし、別の箇所で定義された関数を再定義するLispファイルに対しては、 いずれにしても、これではモジュール化も見通しもよくありません。 アドバイズ機能(see section 16. Emacs Lisp関数のアドバイス)を使えば、見通しがよくなります。


11.9 インライン関数

defunのかわりにdefsubstを使うことで、 インライン関数(inline function)を定義できます。 インライン関数は、1つの点を除いて、普通の関数と同様に動作します。 そのような関数の呼び出しをコンパイルすると、 関数定義は呼び出し側で展開されます。

関数を展開すると明示的な呼び出しが高速になります。 しかし、それには欠点もあります。 その1つは、柔軟性を減らすことです。 関数の定義を変更しても、コンパイルし直すまでは、 すでに展開された呼び出しは古い定義を使い続けます。 関数を再定義できる柔軟性はEmacsでは重要な機能ですから、 速度が本当に重要でなければ、関数を展開すべきではありません。

別の欠点は、大きな関数を展開すると、コンパイルした関数のサイズが ファイル内でもメモリ上でも増加します。 インライン関数のスピードの利点は、小さな関数でもっとも大きいので、 一般には大きな関数を展開すべきではありません。

インライン関数が実行するのと同じコードに展開するようにマクロを定義する ことも可能です。 (see section 12. マクロ。) しかし、マクロは式で直接使った場合に制限されます。 マクロは、applymapcarなどで呼び出せません。 さらに、普通の関数をマクロに変換するには、多少の作業が必要です。 普通の関数をインライン関数に変換するのはとても簡単です。 単に、defundefsubstで置き換えるだけです。 インライン関数の各引数は、ちょうど1回だけ評価されるので、 マクロのように本体で引数を何回使うかを考慮する必要はありません。 (see section 12.6.1 マクロ引数の複数回評価。)

インライン関数は、マクロと同様に、 同じファイル内の定義位置よりうしろで使われ展開されます。


11.10 関数に関連したその他の話題

関数呼び出しと関数定義に関連したいくつかの関数の一覧をあげておきます。 これらは別の場所で説明してありますが、相互参照をあげておきます。

apply
see section 11.5 関数呼び出し

autoload
see section 14.4 自動ロード

call-interactively
see section 20.3 対話的呼び出し

commandp
see section 20.3 対話的呼び出し

documentation
see section 23.2 説明文字列の参照

eval
see section 8.4 評価(eval)

funcall
see section 11.5 関数呼び出し

function
see section 11.7 無名関数

ignore
see section 11.5 関数呼び出し

indirect-function
see section 8.2.4 シンボルの関数間接

interactive
see section 20.2.1 interactiveの使い方

interactive-p
see section 20.3 対話的呼び出し

mapatoms
see section 7.3 シンボルの作成とインターン

mapcar
see section 11.6 マップ関数

map-char-table
see section 6.6 文字テーブル

mapconcat
see section 11.6 マップ関数

undefined
see section 21.7 キー探索


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

This document was generated by Akihiro Sagawa on January, 21 2003 using texi2html