| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
Lispプログラムは、主にLisp関数から構成されます。 本章では、関数とはなにか、引数をどのように受け取るのか、 どのように関数を定義するのかを説明します。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
一般的には、関数とは、引数(arguments)と呼ばれる値を与えられ、 計算を行うための規則です。 この計算結果を関数の値と呼びます。 計算では副作用、つまり、変数の値やデータ構造の内容に継続する変更 を伴うこともできます。
Emacs Lispの関数や関数のようなオブジェクトに関する重要な用語をあげておきます。
carやappendなどのCで書いた Lispから呼び出し可能な関数である。 これらの関数は、組み込み関数とかsubrsとも呼ぶ。 (スペシャルフォームは基本関数とも考えられる。)
関数を基本関数として実装する理由は、 それが基本的なものである、 それがオペレーティングシステムの機能に対する 低レベルのインターフェイスを提供する、 あるいは、高速に動作する必要があるからである。 基本関数を変更したり追加する唯一の方法は、 Cソースを変更してエディタを再コンパイルすることである。 see 節 C.5 Emacs基本関数の書き方。
command-executeが起動できるオブジェクトであり、 キー列に対して定義できる。 いくつかの関数はコマンドである。 Lispで書いた関数に対話宣言(see 節 20.2 コマンドの定義)が含まれているとき、 その関数はコマンドである。 そのような関数は、他の関数と同様にLisp式から呼び出すことができる。 その場合、関数がコマンドであるという事実は関係ない。
キーボードマクロ(文字列かベクトル)もコマンドであるが、 それらは関数ではない。 シンボルの関数定義がコマンドであれば、シンボルはコマンドである。 そのようなシンボルは、M-xで起動できる。 シンボルの定義が関数であれば、シンボルは関数でもある。
tを返す。tを返す。
(subrp 'message) ;
|
tを返す。 たとえば、つぎのとおり。
(byte-code-function-p (symbol-function 'next-line))
=> t
|
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
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.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
Lispで書いた関数(『ラムダ式』)はつぎのようなリストです。
(lambda (arg-variables...) [documentation-string] [interactive-declaration] body-forms...) |
ラムダ式の先頭要素は、つねにシンボルlambdaです。 このシンボルは、リストが関数を表すことを示します。 関数はlambdaで始まると定義してあるのは、 他の目的向けの他のリストが誤って正しい関数とならないようにするためです。
第2要素は、シンボルのリスト、つまり、引数変数名です。 これをラムダリスト(lambda list)と呼びます。 Lisp関数が呼ばれると、引数値をラムダリストの変数に対応させ、 指定した値を持つローカル束縛になります。 See 節 10.3 ローカル変数。
説明文字列は、関数定義の内側にあるLisp文字列オブジェクトであり、 Emacsのヘルプ機能に対して関数を記述します。 See 節 11.2.4 関数の説明文字列。
対話宣言は、(interactive code-string)の形式のリストです。 この宣言は、関数が対話的に使われたときに、 どのように引数を与えるかを指定します。 この宣言を有する関数をコマンド(commands)と呼びます。 コマンドは、M-xで呼び出したり、キーにバインドできます。 このように呼ばれることを意図していない関数には、 対話宣言を付けてはいけません。 対話宣言の書き方については、See 節 20.2 コマンドの定義。
残りの要素は、関数の本体(body)です。 関数の動作を行うLispコードです (Lispプログラマとしては、『評価するべきLispフォームのリスト』という)。 関数が返す値は、本体の最後の要素が返す値です。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
つぎの関数を考えてみましょう。
(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 節 10.3 ローカル変数)を使って、 ローカル変数を作ってそれらに値を与えても、同じ結果を得られます。 さらに、letは見通しがよく使いやすいです。 実用上、ラムダ式は、シンボルの関数定義として格納して名前付き関数を作るか、 他の関数に引数として渡します(see 節 11.7 無名関数)。
しかしながら、スペシャルフォームletがなかった初期のLispでは、 ラムダ式を明示的に呼び出すことはとても便利でした。 その頃では、ラムダ式はローカル変数を束縛し初期化する唯一の方法でした。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
単純な関数の例(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) |
これは、aとbに最初の2つの実引数を束縛し、これらは必須です。 さらに1個か2個の引数を指定すると、 それらは、それぞれcとdに束縛します。 最初の4個よりあとの引数はリストにまとめ、 eにそのリストを束縛します。 引数が2個だけであると、cはnilです。 引数が2個か3個だけであると、dはnilです。 引数が4個以下であると、eはnilです。
省略可能な引数のあとに必須引数を指定する方法はありませんし、 それには意味がありません。 なぜそうなのかを理解するために、上の例で、 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
|
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
ラムダ式には、ラムダリストの直後に 説明文字列(documentation string)があってもかまいません。 この文字列は関数の実行には影響しません。 コメントのようなものですが、Lisp内部に現れる系統的なコメントであり、 Emacsのヘルプ機能が使用します。 documentation-stringの参照方法については、See 節 23. 説明文。
読者のプログラムの関数すべてに、 たとえ内部的に使用されるものであっても説明文字列を与えることはよいことです。 説明文字列はコメントに似ていますが、参照するのはもっと簡単です。
説明文字列の先頭行は、その1行で完結しているべきです。 というのは、aproposは先頭行だけを表示するからです。 関数の機能をまとめた1つか2つの文にしましょう。
説明文字列の先頭は、ソースファイル上では普通字下げしてあるでしょうが、 それらの空白は文字列を始めるダブルクォートのまえにありますから、 それらは文字列の一部ではありません。 説明文字列の残りの行を字下げして、 プログラムソース上でテキスト行が揃うようにする人もいます。 しかし、それはまちがいです。 後続の行の字下げは文字列の内側にあります。 ソースファイルで綺麗に見えても、 ヘルプコマンドの表示では不恰好になります。
関数の必須の構成要素(本体)があとに続くのに、 説明文字列を省略できるのを不思議に思うかもしれません。 文字列を評価すると、副作用なしに、その文字列を返すので、 それが本体の最後のフォームでなければ、なんの効果もありません。 したがって、実用上、本体の最初のフォームと 説明文字列を混同することはありません。 本体のフォームが文字列だけであると、 それは戻り値でもあり説明文字列でもあります。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
ほとんどの計算機言語では、各関数には名前があります。 名前のない関数という考えは本質的ではありません。 Lispでは、もっとも厳密にいえば、関数には名前はありません。 関数は、先頭要素が単にlambdaであるリスト、 バイトコード関数オブジェクト、あるいは、基本関数のsubrオブジェクトです。
しかしながら、シンボルは関数の名前として働きます。 シンボルの関数セル(function cell、see 節 7.1 シンボルの構成要素)に 関数を入れると、このようになります。 そうすると、シンボルそのものは正当な呼び出し可能な関数となり、 関数セルが参照するリストやsubrオブジェクトと等価になります。 関数セルの内容をシンボルの関数定義(function definition)とも呼びます。 シンボルのかわりにシンボルの関数定義を使う処理を シンボルの関数間接(symbol function indirection)と呼びます。 See 節 8.2.4 シンボルの関数間接。
実用上、ほとんどすべての関数には、このようにして名前が付いていて、 その名前で参照します。 たとえば、シンボルcarは、 その関数セルに基本関数のsubrオブジェクト#が格納してあるので、 その動作を行う関数として動作します。
関数に名前を与えるのは、Lisp式からその名前で参照できると便利だからです。 #のような基本関数のsubrオブジェクトでは、 名前はそれらを参照する唯一の方法です。 そのようなオブジェクトには入力構文はありません。 Lispで書いた関数では、明示的なラムダ式より名前を使うほうがより便利です。 また、関数に名前があればそれを参照できます。 つまり、再帰呼び出しができます。 関数の名前をその定義そのものに書くことは、 関数定義がそれ自身を指すようにする (これは不可能ではないにしても、実用上はさまざまな欠点がある)よりは、 とても便利です。
関数を指名するシンボルで関数をしばしば識別します。 たとえば、しばしば『関数car』といって、 シンボルcarと関数定義である基本関数のsubrオブジェクトとを区別しません。 ほとんどの目的には、区別する必要はありません。
たとえそうであっても、関数に一意な名前は必要ないことを 心に留めておいてください。 関数オブジェクトは普通1つのシンボルの関数セルだけに現れますが、 これは単なる便法です。 fsetを使って、複数のシンボルに格納するのは簡単です。 そうすると、各シンボルは同じ関数を同等に指名します。
関数名として使うシンボルは、変数としても使えます。 シンボルのこれら2つの使い方は独立していて衝突しません。 (SchemeなどのLispの方言のなかには、 シンボルの値とその関数定義を区別しないものもある。 変数としてのシンボルの値は、その関数定義でもある。) シンボルに関数定義を与えていないと、そのシンボルを関数としては使えません。 これは、シンボルに変数としての値があるかどうかには関係しません。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
関数を作成するときには、普通、関数に名前を与えます。 これを関数を定義すると呼び、 スペシャルフォームdefunで行います。
defunは、新たにLisp関数を定義する普通の方法である。 これは、シンボルnameをつぎのような関数として定義する。
(lambda argument-list . body-forms) |
defunは、このラムダ式をnameの関数セルに格納する。 値nameを返すが、普通、これは無視する。
前述(see 節 11.2 ラムダ式)のように、 argument-listは引数名のリストであり、 キーワード&optionalや&restが入っていてもよい。 また、body-formsの最初の2つは、説明文字列と対話宣言でもよい。
同一のシンボルnameを変数として使っていても衝突はない。 というのは、シンボルの値セルは関数セルとは独立だからである。 see 節 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などの基本関数であっても、 なんの躊躇も注意もせずに再定義してしまう。 既存関数の再定義は注意深く行うが、 不本意な再定義と熟考した再定義を区別する方法はない。
defaliasを使う正しい場所は、 特定の関数名が定義されている場所である。 特に、ロード中のソースファイルで明示的に名前が現れている場所である。 というのは、defaliasは、defunと同様に、 関数が定義されたファイルを記録するからである(see 節 14.7 アンロード)。
一方、他の目的で関数定義を操作するプログラムでは、 そのような記録を保持しないfsetを使うのがよい。
defunのように関数を定義し、かつ、 Lispコンパイラに関数定義を展開するように指示する defsubstも参照してください。 See 節 11.9 インライン関数。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
関数を定義することは、全体の半分でしかありません。 関数を呼ぶまでは、つまり、実行を命じなければ、関数はなにもしません。 関数呼び出しは起動(invocation)ともいいます。
関数を起動するもっとも一般的な方法は、リストを評価することです。 たとえば、リスト(concat "a" "b")を評価すると、 関数concatを引数"a"と"b"で呼び出します。 評価についてはSee 節 8. 評価。
読者のプログラムで式としてリストを書くときには、 呼び出す関数名を読者のプログラムに書きます。 つまり、プログラムを書くときに、 どの関数をどれだけの引数で呼び出すかを指定できることを意味します。 これが、普通にしたいことでしょう。 呼び出す関数を実行時に計算する必要がある場合もあるでしょう。 それには、関数funcallを使います。 渡す引数の個数を実行時に決定する必要があるときには、 applyを使います。
funcallは、functionをargumentsで呼び出し、 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: #
|
これらの例をapplyの例と比較してほしい。
applyは、funcallのように、 functionをargumentsで呼び出すが、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関数にとっては、引数として関数を受け取ったり、 データ構造(特に、フック変数や属性リスト)内の関数を探して funcallやapplyを使ってそれを呼び出すことは一般的です。 関数引数を受け付ける関数を しばしばファンクショナル(functionals)と呼びます。
場合によっては、ファンクショナルを呼び出すときには、 引数としてなにもしない関数(no-op)を指定できると有用です。 つぎのものは、2種類のなにもしない関数です。
nilを返す。| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
マップ関数(mapping function)は、 リストや他の集まりの各要素に指定した関数を適用します。 Emacs Lispにはそのような関数がいくつかあります。 mapcarとmapconcatはリストを走査するもので、ここで説明します。 オブジェクト配列obarray内のシンボルについて マップする関数mapatomsについては、 See 節 7.3 シンボルの作成とインターン。
これらのマップ関数では、文字テーブルは扱えません。 というのは、文字テーブルは疎な配列であり、その添字範囲も非常に大きいからです。 文字テーブルの疎な性質を考慮して文字テーブルについてマップするには、 関数map-char-table(see 節 6.6 文字テーブル)を使います。
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")
;;
|
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"
|
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
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))) |
quoteに等価である。 しかし、これは、Emacs Lispコンパイラに対しては注意書きとして働き、 function-objectを関数としてのみ使う意図があり、 したがって、コンパイルしても安全であることを意味する。 8.3 クォートのquoteと比較してほしい。functionと無名関数を用いた実際的な例は、 23.2 説明文字列の参照のdocumentationを参照してください。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
シンボルの関数定義(function definition)とは、 シンボルの関数セルに格納されたオブジェクトです。 ここで説明する関数は、シンボルの関数セルを参照したり、調べたり、 設定したりします。
8.2.4 シンボルの関数間接の関数indirect-functionも参照してください。
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と違うことに注意してください。 シンボルnilもvoidもLispオブジェクトであり、 それらは他のオブジェクトと同様に関数セルに格納できます (そして、それらをdefunで定義しておけば、正しい関数である)。 空の関数セルには、どんなオブジェクトも含まれていません。
シンボルの関数定義が空かどうかはfboundpで調べることができます。 シンボルに関数定義を与えたあとでも、 fmakunboundを使ってふたたび空にできます。
tを返し、 さもなければnilを返す。 オブジェクトが正しい関数であるかどうか検査しない。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
|
この関数の普通の3つの使い方はつぎのとおり。
fsetのかわりにdefaliasを使うべきである。 see 節 11.4 関数を定義する。)
defunではできない。 たとえば、fsetを使って、s1に関数定義として 別のシンボルs2を与えることができる。 すると、s1は、s2の現在の定義の別名として働く。 (これをs1の定義と考えるのであれば、 やはり、fsetのかわりにdefaliasを使う。)
defunが基本関数でなかったならば、 fsetを使って(マクロとして)Lispでdefunを書くことができる。これらの使用例を示す。
;; |
既存の関数定義を拡張する関数を書くときには、 つぎのような常套句を使うこともあります。
(fset 'old-foo (symbol-function 'foo)) (defun foo () "Just like old-foo, except more so." (old-foo) (more-so)) |
fooが自動ロードと定義されていると、これは正しく動作しません。 そのような場合には、fooがold-fooを呼び出すと、 Lispはファイルをロードしてold-fooを定義しようとします。 しかし、これはold-fooではなくfooを定義するので、 正しい結果を得られません。 この問題を回避する唯一の方法は、 fooの古い定義を移すまえに、確実にファイルをロードしておくことです。
しかし、別の箇所で定義された関数を再定義するLispファイルに対しては、 いずれにしても、これではモジュール化も見通しもよくありません。 アドバイズ機能(see 節 16. Emacs Lisp関数のアドバイス (2003/10/30))を使えば、見通しがよくなります。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
defunのかわりにdefsubstを使うことで、 インライン関数(inline function)を定義できます。 インライン関数は、1つの点を除いて、普通の関数と同様に動作します。 そのような関数の呼び出しをコンパイルすると、 関数定義は呼び出し側で展開されます。
関数を展開すると明示的な呼び出しが高速になります。 しかし、それには欠点もあります。 その1つは、柔軟性を減らすことです。 関数の定義を変更しても、コンパイルし直すまでは、 すでに展開された呼び出しは古い定義を使い続けます。 関数を再定義できる柔軟性はEmacsでは重要な機能ですから、 速度が本当に重要でなければ、関数を展開すべきではありません。
別の欠点は、大きな関数を展開すると、コンパイルした関数のサイズが ファイル内でもメモリ上でも増加します。 インライン関数のスピードの利点は、小さな関数でもっとも大きいので、 一般には大きな関数を展開すべきではありません。
インライン関数が実行するのと同じコードに展開するようにマクロを定義する ことも可能です。 (see 節 12. マクロ。) しかし、マクロは式で直接使った場合に制限されます。 マクロは、applyやmapcarなどで呼び出せません。 さらに、普通の関数をマクロに変換するには、多少の作業が必要です。 普通の関数をインライン関数に変換するのはとても簡単です。 単に、defunをdefsubstで置き換えるだけです。 インライン関数の各引数は、ちょうど1回だけ評価されるので、 マクロのように本体で引数を何回使うかを考慮する必要はありません。 (see 節 12.6.1 マクロ引数の複数回評価。)
インライン関数は、マクロと同様に、 同じファイル内の定義位置よりうしろで使われ展開されます。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
関数呼び出しと関数定義に関連したいくつかの関数の一覧をあげておきます。 これらは別の場所で説明してありますが、相互参照をあげておきます。
applyautoloadcall-interactivelycommandpdocumentationevalfuncallfunctionignoreindirect-functioninteractiveinteractiveの使い方。
interactive-pmapatomsmapcarmap-char-tablemapconcatundefined| [ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |