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

3. 関数定義の書き方

Lisp インタプリタがリストを評価する場合、まずリストの最初のシンボルが関 数定義を持っているかどうか、別の言い方をするとシンボルから関数定義へのポ インタがあるかを見にいく。もしそうであれば、計算機はその定義に含まれる命 令を実行する。関数定義を持つシンボルのことを単に関数と言う。 (ただし、正 確には定義が関数なのであり、シンボルはそれを指しているだけである。)

プリミティブな関数について  幾つかの関数は C で書かれている
3.1 特殊形式 defun  
3.2 関数定義のインストール  
3.3 関数をインタラクティブにする  
3.4 interactive の他のオプション  
3.5 コードをずっとインストールしておくには  
3.6 let  局所変数の作成と初期化
3.7 特殊形式 if  if とは?
3.8 If--then--else 式  
3.9 Lisp における真と偽  
3.10 save-excursion  ポイントやマークの位置、及びバッファの保存
3.11 復習  
3.12 練習問題  

プリミティブな関数について

全ての関数は C 言語で書かれた少数の プリミティブ (primitive) な関数を除いて、別の関数を使って定義されている。関数を書く場合にはそれを Emacs Lisp を使って書き、他の関数を部品として使うことになる。あなたが使 う関数はそれ自身 (あなたが書いたものを含めて) Emacs Lisp で書かれたもの かもしれないし、C で書かれたプリミティブなものかもしれない。プリミティブ な関数も Emacs Lisp で書かれているのものと全く同じように使われ、同じよう に振舞う。これらが C で書かれているのは、十分な能力を持ち、C を走らせる ことが出来るどの計算機でも簡単に GNU Emacs が動くようにするためである。

再度強調しておくけれども、Emacs Lisp のコードを書く場合、C で書かれた関 数を使う場合と Emacs Lisp で書かれた関数を使う場合とで区別をつける必要は 全くない。違いは全く無視して構わない。私がこの区別のことを書いたのは、単 に知っていた方が面白いからである。実際、調査でもしない限り、既に書かれて いる関数が Emacs Lisp で書かれているか C で書かれているかは判断出来ない。


3.1 特殊形式 defun

Lisp では mark-whole-buffer のようなシンボルには、その関数が呼ば れた時に計算機が何をすべきかを教えるコードが付随している。このコードのこ とを 関数定義 (function definition) といい、シンボル defun で始まるS式を評価した時に作られる。(これは define function の略である。) defun は通常とは別の引数の評価の仕方をす るために、特殊形式 (special form) と呼ばれる。

この後に続く幾つかのセクションでは、例えば mark-whole-buffer のよ うに Emacs のソースコードの中から関数定義を見ていくことにする。が、この セクションではもっと単純な関数定義を説明し、それがどんなものなのかを、ま ず理解してもらうことにしよう。簡単な例にするために算数についての例にする。 算数を使った例が嫌いな人達もいるとは思うが、あなたがそうだとしてもがっく りくる必要はない。この入門書の残りに出てくる殆どのコードには算数とか数学 は出てこない。出てくる例は、大体が何がしかの意味でテキストに関わるもので ある。

関数定義は defun という単語に続けて、次のような最大五つの部分を加 えたものである。

  1. 関数定義が付けられるシンボルの名前。

  2. 関数に渡される引数のリスト。もし一つも引数をつけない場合、ここは空リスト () にする。

  3. 関数を説明する文章。(技術的には省略可能であるが、書くことが強く望まれて いる。)

  4. 省略可能。この関数をインタラクティブにする、つまり M-x に続けて関 数名をタイプするか、適当なキーないしはキーコードをタイプすることで使うこ とが出来るようにするためのS式を書く。

  5. 計算機に何をすべきかを命令するコードを書く。即ち、関数定義の本体である。

この関数定義の五つの部分の各々にスロットを割当てて、次のようなテンプレー トにして整理しておくと役に立つだろう。

 
(defun 関数名 (引数...)
  "オプションの説明文字列..."
  (interactive 引数情報) ; 省略可能
  本体...)

一つの例として、引数に7をかける関数のコードを挙げる。(この例はインタラ クティブではない。関数をインタラクティブにする, を 見よ。)

 
(defun multiply-by-seven (number)
  "Multiply NUMBER by seven."
  (* 7 number))

この定義は括弧と defun というシンボルで始まり、関数の名前が続いて いる。

関数の名前の次にはこの関数に渡される引数のリストが来る。このリストは 引数リスト (argument list) と呼ばれる。今の場合、このリスト の要素は number というシンボル一つだけである。この関数が使われる 時には、このシンボルは関数の引数として使われる値にバインドされる。

引数の名前として number という単語を選ぶ代わりに他の名前を選んで も良い。例えば multiplicand という単語でも良かった。私が `number' という単語を選んだのは、このスロットにどういう種類の値が要求されるかが分 るからである。しかし、このスロットに置かれる値がこの関数の働きの中でどう いう役割を果たすかを示すという意味で `multiplicand' という単語を選ぶのも 悪くなかった。foogle としても構わないが、これは良い選択だとは思え ない。これでは何のことか分らない。どういう名前にするかはプログラマ次第だ が、関数の意味を明確にするようなものが選ばれるべきである。

実の所、引数リストの中のシンボルにはどんな名前でも選ぶことが出来る。たと え、他の関数の中で使われているシンボル名であっても良いのである。引数リス トの中で使われる名前は、その特定の定義の中だけでしか使われないプライベー トなものであるからだ。この定義の中では、他のどんな関数定義の中にある同じ 名前のシンボルとも異なる実体を指すのである。例えば、あなたが家族の間では `Shorty' というニックネームをつけられているとしよう。この場合、あなたの 家族の誰かが `Shorty' という場合には、それはあなたのことを指している。し かしあなたの家族以外の所、例えば映画の中などでは `Shorty' というのは別の 誰かを指している。引数リストの中の名前はその関数の中だけで通用するものな ので、この関数の本体の中でそのシンボルの値をどう変えたとしても外での値に は何の影響もないというわけだ。似たような効果が let を使ったS式の 場合でも見られる。(let, 参照。)

引数リストの次には、関数についての説明である、説明文字列が来る。これは C-h f に続けてその関数の名前をタイプした時に表示される内容であ る。ついでに言っておくと、あなたが説明文字列を書く場合には、最初の一行は 完結した一つの文にすべきである。というのも、apropos のような幾つ かのコマンドは複数行に渡って書かれた説明文の文字列のうちの最初の一行だけ しか表示しないからである。また、二行目も書く場合はこれをインデントすべき ではない。何故なら、C-h f (describe-function) を使ったとき に変な表示のされかたをするからである。説明文字列は省略可能ではあるが、大 変役に立つものなので、関数を書く際にはなるべく書くようにすべきである。

さっきの例での三行目は関数定義の本体をなしている。 (普通の関数定義は勿論 これよりもっと長い。) 今の場合、この本体は (* 7 number) というリ ストであり、number の値に7をかけることを定めている。(Emacs Lisp で は * は掛け算を表わす関数である。同様に + は足し算を表わす。)

multiply-by-seven という関数を使う場合、引数 number はあな たが実際に使いたい値に評価される。次の例では multiply-by-seven の 使い方を示している。ただし、まだこれを評価しないように!

 
(multiply-by-seven 3)

次のセクションで詳しく述べるが、今の例ではシンボル number に3とい う値が与えられた、ないしは「バインドされた」わけである。number は 関数定義の括弧の中にあるが、multiply-by-seven という関数に渡され る引数は括弧の外にあることに注意しておこう。関数定義の中に括弧があるのは、 計算機が引数のリストが何処で終わり、残りの部分が何処から始まるかを知るた めにあるのである。

この例を評価すると多分エラーメッセージが出るだろう。(試してみよう!) こ れは、我々は確かに関数定義を書いたのだが、まだその定義を計算機に伝えてい ない---つまり、まだ Emacs にこの関数定義をインストール (もしくはロード) していない---ためである。インストールの仕方については次のセクションで説 明する。


3.2 関数定義のインストール

もし Emacs の Info の中でこれを読んでいるのなら、先に関数の定義を評価し、 次に (multiply-by-seven 3) を評価することで、 multiply-by-seven という関数を試してみることが出来る。この下に関 数定義の写しを書いておくので、その直後にカーソルを持っていって C-x C-e とタイプしよう。そうすると、エコー領域に multiply-by-seven と表示される。(これは関数定義を評価した時に返す値はその関数の名前である ことを示している。) 同時に、こうすることで関数定義がインストールされる。

 
(defun multiply-by-seven (number)
  "Multiply NUMBER by seven."
  (* 7 number))

この defun を評価することで、たった今 Emacs に multiply-by-seven がインストールされた。今ではこの関数は forward-word やあなたが編集に使う他の関数と全く同様、Emacs の一部 である。(multiply-by-seven はあなたが Emacs を終了するまでインス トールされたままになっている。このコードを Emacs を起動するたびに再ロー ドする方法については、 コードをずっとインストールしておくには, を見よ。)

multiply-by-seven をインストールしたことでどうなったかは、次の例 を評価することで見ることが出来る。カーソルを次のS式の直後に持っていって C-x C-e とタイプしてよう。エコー領域には21という数字が表示されたは ずだ。

 
(multiply-by-seven 3)

もし望むなら、C-h f (describe-function) に続けてこの関数の 名前 multiply-by-seven をタイプすることで、この関数定義の説明を読 むことも出来る。そうすると、`*Help*' というウィンドウに次のように表 示される。

 
multiply-by-seven: Multiply NUMBER by seven.

(スクリーンを元の一つのウィンドウに戻すには、C-x 1 とタイプすれば 良い。)

3.2.1 関数定義の変更  


3.2.1 関数定義の変更

multiply-by-seven の中のコードを変更したい場合は、単にそれを書き 直すだけである。古いものを新しいバージョンに置き換えるには、その関数定義 をもう一度評価すれば良い。これが Emacs の中のコードを修正する方法である。 極めて単純だ。

例として、multiply-by-seven という関数を7をかけるのではなく、その 数自身を7回加えるものに変更することが出来る。結果としては同じ答えが得ら れるけれども、途中の道筋は違っている。同時にコードの中にコメントを加えよ う。これは Emacs には無視されるけれども、人間が読む場合には便利だなあと か解りやすいとか思うはずだ。今回は、これが二番目のバージョンであるという コメントを書いておくことにする。

 
(defun multiply-by-seven (number) ; 二番目のバージョン
  "Multiply NUMBER by seven."
  (+ number number number number number number number))

コメントはセミコロン `;' の後に続いている。Lisp では、セミコロンの 後に続く全てのものはコメントである。行の終わりがコメントの終わりになる。 二行以上に渡ってコメントを書きたい場合は各々の行をセミコロンで始める。

`.emacs' の書き方,及び section `Comments' in The GNU Emacs Lisp Reference Manual, にコメントについて、より詳しく説明されている。

今のバージョンの multiply-by-seven 関数をインストールするには最初 のものをインストールした時と全く同じようにして評価してやれば良い。つまり、 カーソルを最後の括弧のすぐ後に持ってきて C-x C-e とタイプすれば良 いのである。

まとめると、Emacs でコードを書く方法は次の通りである。まずは関数を書き、 インストールし、テストしてみる。で、不具合を修正したり拡張したりして再 度インストールするという具合だ。


3.3 関数をインタラクティブにする

関数をインタラクティブなものにするには、特殊形式 interactive で始 まるリストを説明文字列のすぐ後に置けばよい。インタラクティブな関数はユー ザーが M-x に続けて関数の名前をタイプすることで呼び出すことが出来る。 あるいは、その関数にバインドしたキーをタイプしてもよい。例えば、 C-n とタイプすると next-line が呼ばれるし、C-x h とタ イプすると mark-whole-buffer が呼ばれる。

面白いことに、インタラクティブな関数をインタラクティブに呼び出すと、返さ れる値は自動的にはエコー領域に表示されない。これはインタラクティブな関数 はしばしば、単語分や一行分前に進んだりするなどの副作用を目的に呼ぶのであっ て、返される値のことは気にしないからである。もしキーをタイプするごとにエ コー領域に値が表示されたら、随分と欝陶しいことだろう。

特殊形式 interactive を使いつつエコー領域に値を表示する方法につい て説明するために、 multiply-by-seven のインタラクティブバージョン を作ってみよう。

これがそのコードである。

 
(defun multiply-by-seven (number) ; インタラクティブバージョン
  "Multiply NUMBER by seven."
  (interactive "p")
  (message "The result is %d" (* 7 number)))

このコードはカーソルをコードのすぐ後に持っていって C-x C-e とタイ プすることでインストール出来る。ちゃんと、この関数の名前がエコー領域に表 示されただろうか。次からこのコードを使うには、まず C-u に続けて数 をタイプし、その後に M-x multiply-by-seven とタイプして RET を押せば良い。エコー領域に、`The result is ...' という文に続い て計算結果が表示されるはずだ。

もっと一般的に言うと、このような関数は次の二通りの内どちらかの方法で呼び 出すことが出来る。

  1. まず渡されるべき数を含む前置引数をタイプする。ついで M-x と関数名 をタイプする、例えば C-u 3 M-x forward-sentence、とする。

  2. あるいは、その関数にバインドされたキーないしはキーコードをタイプする。例 えば C-u 3 M-e

今挙げた二つの例では、どちらの場合もポイントが3つの文だけ前方に移動する。 (この関数を例として扱った理由は、multiply-by-seven はどのキーにも バインドされてはいないので、キーバインディングの例としては使えないからで ある。)

(コマンドをキーにバインドする方法については 幾つかのキーバインディング, を参照のこと。)

前置引数をインタラクティブな関数に渡すには、M-3 M-e のように META キーに続けて数をタイプする方法と、C-u 3 M-e のように C-u に続けて数をタイプする方法とがある。(もし C-u の後に何も 数をタイプしなかった場合、デフォルトでは4が渡される。)

3.3.1 インタラクティブな multiply-by-seven  インタラクティブバージョン


3.3.1 インタラクティブな multiply-by-seven

インタラクティブバージョンの multiply-by-seven の中の特殊形式 interactive の使い方と、関数 message を見てみよう。もう一 度この関数の定義を書いておく。

 
(defun multiply-by-seven (number) ; インタラクティブバージョン
  "Multiply NUMBER by seven."
  (interactive "p")
  (message "The result is %d" (* 7 number)))

この関数の中ではS式 (interactive "p") は二つの要素からなるリスト である。"p" は Emacs に対し、この関数に前置引数を渡し、その値を関 数の引数として使うことを知らせるためのものである。

引数は数になる。これは number というシンボルが

 
(message "The result is %d" (* 7 number))

という行の中である数にバインドされるという意味である。例えば、前置引数が 5だとすると、Lisp インタプリタはこの行を次のように評価することになる。

 
(message "The result is %d" (* 7 5))

(もし、この文章を GNU Emacs の中で読んでいるなら、このS式をあなた自身で 評価することが出来る。) 最初にインタプリタは内側にあるリストを評価する。 今の場合は (* 7 5) である。これは値35を返す。次にその外側のリスト が評価される。つまり、二番目及び残りの要素の値が message という関 数に渡される。

前に見たように、message は特に一行のメッセージをユーザに送るため の Emacs Lisp の関数であった。(関数 message, 参 照。) 手短に言うと、関数 message は最初の引数をエコー領域に表示す るのだが、`%d', `%s', 及び `%c' だけは例外で、これらの制 御文字列の内のどれかが現れた時は、残りの二番目以降の引数を順に見に行き、 その値をこれらがある場所に挿入して表示する。

multiply-by-seven という関数では、制御文字列として `%d' が使 われ、これは数を要求する。今の場合ならその値は (* 7 5) を評価して 返された35という数である。その結果、`%d' の場所には35が表示され、メッ セージは `The result is 35' となるわけである。

(ここで注意だが、multiply-by-seven という関数を呼んだ場合、メッセー ジは引用符無しで表示される。しかし message をそのまま呼んだ場合に は、二重引用符で囲まれている。これは message が返す値そのものは message が先頭にあるS式を評価した場合にエコー領域に表示されるも ので、これには引用符がついているのだが、これが他の関数の中に埋め込まれて 使用された場合、表示されるものは message が副作用として出力するも ので、これには二重引用符がついていないからである。)


3.4 interactive の他のオプション

上の例で multiply-by-seveninteractive の引数として "p"を使っている。この引数がある場合、Emacs はあなたが、C-u に続けて数をタイプするか、Meta キーに続けて数をタイプするかして、 このコマンドに引数として数を渡すものと解釈する。Emacs はこのような interactive の引数を20種類以上用意している。大抵の場合、これらの オプションの内にあなたが望む通りの情報を関数に渡すものが一つか二つはある はずだ。(section `Code Characters for interactive' in The GNU Emacs Lisp Reference Manual, 参照。)

例えば `r' という文字の場合、Emacs はその関数にその時のリージョンの 最初と最後の位置 (その時のポイントとマークの値) を二つの別々の引数として 渡す。使い方は例えばこんな風だ。

 
(interactive "r")

あるいは、`B' であれば、Emacs はバッファの名前を聞いてきて、入力し た値をその関数に渡す。もう少し詳しく言うと、Emacs はミニバッファにプロン プトを出して、ユーザーに名前を要求する。この時のプロンプトには `B' に続く値が使われるので、例えば、"BAppend to buffer: " とか書い ておく。Emacs はただ単にプロンプトを出すだけではなくて、判断出来る分だけ の入力があれば、TAB キーを押すことで補完までしてくれる。

関数に二つ以上の引数を渡したい場合、interactive に続けて複数の文 字列を付け加えることで、各々の引数に情報を渡すことが出来る。この場合その 情報は、各々の引数に interactive に書いたのと同じ順序で渡される。 付け加える文字列は、各々の部分を `\n', 即ち改行コードで区切る。例え ば "BAppend to buffer: " に続けて `\n'`r' を書い たりする。こうすると、Emacs はプロンプトでバッファ名を要求すると同時にポ イントとマークの値もその関数に渡してくれる---三つの引数全てを渡してくれ るのである。

この場合の関数定義は次のような形式になる。buffer, start, 及び、endinteractive がバッファとその時のリージョンの 始まりと終わりをバインドするシンボルである。

 
(defun 関数名 (buffer start end)
  "説明文字列..."
  (interactive "BAppend to buffer: \nr")
  関数の本体...)

(プロンプトのコロンの後の空白は、プロンプトを出す時の見栄えをよくするた めのものである。append-to-buffer という関数はまさにこのようになっ ている。append-to-buffer の定義, 参照。)

あるいは、もし上に挙げたような特定の文字による引数の与え方があなたの目的 に合わない場合、あなた自身の引数をリストとして interactive に渡す ことも可能である。section `Using Interactive' in The GNU Emacs Lisp Reference Manual, にこの上級テクニックについ てのより詳しい解説がある。


3.5 コードをずっとインストールしておくには

関数定義を評価することで一旦その関数をインストールすると、Emacs を終了す るまでその関数はインストールされたままになっている。一方、次に Emacs の 新しいセッションを開始した時は、その関数定義を再度評価するまでその関数は インストールされない。

ある時点で、Emacs の新しいセッションを始める時に自動的にインストールした いと思うかもしれない。方法は幾つかある。

最後に、もしあなたが Emacs を使う全ての人が欲しくなるようなコードを書い たなら、そのコピーをネットワーク上にポストしたり、Free Software Foundation に送ることが出来る。(そうする場合には、どうかポストする前にコー ドに copy left の注意書きを添付して欲しい。) コピーを Free Software Foundation に送った場合、次の Emacs のリリース時にはそれを含めて配布され るかもしれない。大体において、ここ数年はこのような寄付によって Emacs が 成長してきたのである。


3.6 let

let 式は、Lisp では多くの関数定義の中で必要となる特殊形式である。 非常によく使われるものなので、このセクションの中で let について説 明することにする。

let は、シンボルに値をバインドする際に、Lisp インタプリタがその関 数以外の関数の中で使われている同じ名前の変数と混同しないようにするために 使用される。何故こんな関数が必要なのかということを納得するために、次の様 な状況を考えよう。あなたは自分自身の家を持っていて、それを「家」と呼んで いるとする。例えば、「そろそろ家にもペンキ塗りが必要だなあ」という風に使 うわけである。しかし、あなたが友人宅を訪問し、そこのホストの人が「家」と 言った場合、大抵それはあなたの家ではなく彼の家のことを指している。 即ち、同じ「家」という言葉で言及されてはいても別のものなわけである。もし 彼が彼自身の家のことを言っていて、あなたがあなた自身の家を思い浮かべてい たなら混乱してしまうことであろう。同じことが Lisp についても言える。ある 関数で使われている変数と同じ名前の変数が他の関数でも使われていて、しかも その二つが同じ値を持つことを期待されてはいない時である。

let という特殊形式を使うことでこのような混乱を防ぐことが出来る。 letローカル変数 (local variable) と呼ばれるものを 発生させる。これは、その let 式の外にある同じ名前の変数からは隔離 されている変数である。訪問先のホストの人が「家」という時は彼は彼自身の家 を指しているのであって、あなたの家を指しているのではないのと似たようなも のだ。 (引数リストの中のシンボルも同じような働きをする。特殊形式 defun, を見よ。)

let 式によって発生したローカル変数の値が保持されているのは、その let 式の中だけである。(そして、その中のS式はその let 式の中だけで呼ばれる。) 従って、ローカル変数はその let 式の外には全く影響を与えない。

let は一度に複数の変数を発生させることが出来る。また、let は各々の変数に初期値を設定する。あなたが指定すればその値になるし、そうし なければ nil が設定される。(専門用語では、このことを「その変数に 値をバインドする」と言う。) let は変数を発生させ、それに値をバイ ンドした後、本体のコードを実行し、本体の中の最後のS式の値を返す。(実行 (Execute) するというのは、リストを評価するという意味の専門用語である。こ れはこの単語の「実質的な効果を与える (to give practical effect to)」とい う意味での使い方から来ている (Oxford English Dictionary)。あなた はある動作を引き起こすためにS式を評価しているのだから、この場合「実行」 するというのは評価するのと同義であろう。)

3.6.1 let 式の構成部分  
3.6.2 let 式の例  
3.6.3 let 式の変数宣言の中で初期値を設定しなかった場合  初期値をバインドしないとどうなるか


3.6.1 let 式の構成部分

let というS式は三つの部分からなるリストである。最初の部分はシン ボル let である。二番目の部分は 変数リスト (varlist) と呼ばれるリストであり、各々の要素はシンボルそのものであるか、最初の要素 がシンボルである二つの要素からなるリストであるかのどちらかであり、三番目 の部分は let 式の本体である。本体部分は大抵、一つないしは複数のリ ストからなる。

let 式のテンプレートは次のように書ける。

 
(let 変数リスト 本体...)
変数リストの中のシンボルは特殊形式 let 式によって初期値を設定され る変数である。単独のシンボルそのものの場合は、初期値 nil が設定さ れる。また、最初の要素がシンボルであるような二つの要素からなるリストの場 合、その最初のシンボルに対して Lisp インタプリタが二番目の要素を評価した 時に返される値が設定される。

というわけで、変数リストは (thread (needles 3)) という感じの式に なる。この場合だと、let 式の中で Emacs はシンボル thread を初期値 nil に、シンボル needles を初期値3にバインドする。

let 式を書く場合にすることは、適切なS式を let 式のテンプ レートの中に置くことである。

もし、変数リストが二つ要素のリストからなる場合、といっても大抵はそうなの だが、let 式のテンプレートは次のようになる。

 
(let ((変数 )
      (変数 )
      ...)
      本体...)


3.6.2 let 式の例

次のS式は二つの変数 zebratiger を発生させ、それに対し て初期値を与えている。let 式の本体は関数 message を呼び出 すリストである。

 
(let ((zebra 'stripes)
      (tiger 'fierce))
  (message "One kind of animal has %s and another is %s."
           zebra tiger))

ここで、変数リストは ((zebra 'stripes) (tiger 'fierce)) である。

二つの変数は zebratiger である。各々の変数は二つの要素 からなるリストの最初の要素であり、その値はそのリストの二番目の要素になっ ている。Emacs は変数リストの中で zebrastripes にバイン ドし、また、tigerfierce にバインドしている。この場合、 値は両方とも引用符のついたシンボルである。値は勿論、他のリストや文字列で あっても構わない。let 式の本体は変数を含むリストの後に来る。今の 場合は本体は関数 message を使ったリストであり、エコー領域に文字列 を表示する。

いつものようにカーソルを最後の括弧の直後に置いて C-x C-e とタイプ することで上の例を評価することが出来る。そうすると、エコー領域に次のよう に表示されるだろう。

 
"One kind of animal has stripes and another is fierce."

以前見たように、message 関数は最初の引数を `%s' を除いて表示 する。今の場合、最初の `%s' の所には変数 zebra の値が表示さ れ、二番目の `%s' の所には変数 tiger の値が表示される。


3.6.3 let 式の変数宣言の中で初期値を設定しなかった場合

もし、let 式の中で変数に特定の初期値をバインドしなかったとすると、 それらの値は自動的に nil にバインドされる。次の例を見てみよう。

 
(let ((birch 3)
      pine
      fir
      (oak 'some))
  (message
   "Here are %d variables with %s, %s, and %s value."
   birch pine fir oak))

ここで、変数リストは ((birch 3) pine fir (oak 'some)) である。

このS式をいつもの通りに評価したなら、エコー領域には次のように表示される だろう。

 
"Here are 3 variables with nil, nil, and some value."

この場合には Emacs はシンボル birch に3という数をバインドし、シン ボル pinefir には nil をバインドし、更にシンボ ル oak には some をバインドしている。

let 式の最初の部分で、変数 pinefir は単独のアト ムであり、括弧で囲まれてはいないことに注意しよう。これは、これらの変数を nil, 即ち空リストにバインドするためである。しかし、oak の 方は、some にバインドするので、リスト (oak 'some) の一部と なっている。同様に、birch も3にバインドするので、この数と一緒のリ ストに入っている。(数値は評価された時にそれ自身の値を返すので、引用符は 必要ない。また、message の中にこの数を表示する際は、`%s' ではなくて、 `%d' を使っている。) そして、これら四つの変数がひとつのグループとし てリストの中に入り、let 式の本体と区別されているわけである。


3.7 特殊形式 if

defunlet に続く三つ目の特殊形式は、条件分岐 (conditional) if である。この特殊形式は計算機になんらかの判断をさ せる時に使われる。if を使わなくても関数定義を書くことも出来るが、 頻繁に使うものであるし、重要でもあるので、ここに含めることにする。これは 例えば、beginning-of-buffer という関数の中で使われている。

if の背後にある基本的な考え方は「もし(if)、テストした結果 が真ならば(then)S式を評価する」というものだ。テストした結果が真 でなければ、S式は評価はされない。例えば、「もし、暖かくて太陽が出ていた なら、ビーチへ行こう。」というような判断をするようなものだ。

Lisp で書かれる if 式では、`then' という単語は使われない。テスト と実行は if を最初の要素とするリストの二番目と三番目の要素である。 にも関わらず、if 式のテスト部分は if-part と呼ばれ、二番目 の引数は then-part と呼ばれることが多い。

また、if 式が書かれる場合、真か偽かのテストは普通、if と同 じ行に書かれる。しかし、テストが真であった場合に実行される "then-part" は二行目以降に書かれる。こうすることで、if 式がより読みやすいもの になる。

 
(if 真偽テスト
    テストが真である場合に実行する動作)

真偽テスト (true-or-false-test) の部分は List インタプリタにより評価され るS式である。

次にいつものようにして実行出来る例を挙げておく。テスト部分は5が4よりも大 きいかどうかを判断するものだ。そうであれば、`5 is greater than 4!' というメッセージが表示される。

 
(if (> 5 4) ; if-part
    (message "5 is greater than 4!"))  ; then-part

(関数 > は最初の引数が二番目の引数よりも大きいかどうかを判断し、 そうであれば真を返すものである。)

勿論、実際に使う場合には、if 式のテスト部分は常に (> 5 4) に固定されていたりはしない。その代わりに、少なくとも一つの変数が、前もっ て分らない値にバインドされる。(もし、前もって値が分っているなら、テスト する必要などない!)

例えば、その値は関数定義の引数にバインドされたりする。次の関数定義では、 関数に渡される値は動物の性格である。もし、characteristic にバイン ドされる値が fierce であれば、`It's a tiger!' というメッセー ジが表示される。そうでなければ、nil が返される。

 
(defun type-of-animal (characteristic)
  "Print message in echo area depending on CHARACTERISTIC.  If the
CHARACTERISTIC is the symbol `fierce', then warn of a tiger."
  (if (equal characteristic 'fierce)
      (message "It's a tiger!")))

もし、これを GNU Emacs の中で読んでいるなら、上の関数定義を評価すること で、Emacs にインストールすることが出来る。そうすると、次の二つのS式を評 価して結果を見ることも出来るようになる。

 
(type-of-animal 'fierce)

(type-of-animal 'zebra)

(type-of-animal 'fierce) を評価すると、エコー領域には "It's a tiger" と表示されるはずだ。一方、(type-of-animal 'zebra) を評 価すると、nil が表示される。

3.7.1 関数 type-of-animal の詳細  if 式の例

3.7.1 関数 type-of-animal の詳細

type-of-animal 関数を詳しく見てみよう。

type-of-animal の関数定義は二つのテンプレートのスロットを埋めるこ とで書かれている。一つは全体の関数定義のテンプレートで、もう一つは if 式のテンプレートである。

インタラクティブでない関数の関数定義のテンプレートは次の通りである。

 
(defun 関数名 (引数リスト)
  "説明文字列..."
  本体...)

このテンプレートに当てはめると、各部分は次のようになる。

 
(defun type-of-animal (characteristic)
  "Print message in echo area depending on CHARACTERISTIC.  If the
CHARACTERISTIC is the symbol `fierce', then warn of a tiger."
  本体:  if )

今の場合、関数の名前は type-of-animal である。これは引数を一つ取 る。引数リストの後に、複数行に渡る説明文字列が続く。例の中にもちゃんと説 明文字列を入れるのは、全ての関数定義に説明文字列を入れるのが良い習慣であ るからである。関数定義の本体部分は if 式からなっている。

if 式のテンプレートは次の通りである。

 
(if 真偽テスト
    テストが真の場合に実行する動作)

type-of-animal 関数では、if に関する実際のコードは次のよう になっている。

 
(if (equal characteristic 'fierce)
    (message "It's a tiger!")))

ここで、真偽テストの部分は次のS式である。

 
(equal characteristic 'fierce)

Lisp では、equal は最初の引数と二番目の引数が等しいかどうかを判定 する関数である。二番目の引数は引用符つきのシンボル 'fierce であり、 一番目の引数は characteristic というシンボルの値---つまり、この関 数に渡される引数の値である。

先に行った type-of-animal の最初の実行では、fiercetype-of-animal に渡した。fiercefierce と等しい ので、S式 (equal characteristic 'fierce) は真の値を返す。この場 合は、if は二番目の引数、つまり if 式の then-part である (message "It's tiger!") を評価する。

一方、type-of-animal の二番目の実行では zebra を引数として 渡した。zebrafierce ではないので、then-part は評価され ず、if 式は nil を返す。


3.8 If--then--else 式

if 式は、オプションとして else-part と呼ばれる三番目の引き 数を持つことが出来る。これは真偽テストが偽を返した場合のためのものである。 この場合は if 式の二番目の引数である then-part は評価されず、 代わりに三番目の引数である else-part が評価される。このことは、曇 りの日なんかに、「もし、晴れて暖かかったらビーチに行こう。そうでなかった ら、本でも読むか!」というふうに、代わりの選択肢を考えるのに当てはめてみ れば納得出来るだろう。

"else" という単語は Lisp のコードの中には出てこない。単に、else-part は if 式の中の then-part の次にくる要素というだけである。実際に Lisp のコードを書く場合、else-part は新しく改行してから then-part よりは 少ないインデントで書き始めるのが普通である。

 
(if 真偽テスト
    テストが真の場合に実行する動作)
  テストが偽の場合に実行する動作)

例えば次の if 式は、いつも通りに評価すると、`4 is not greater than 5!' というメッセージを表示するものである。

 
(if (> 4 5) ; if-part
    (message "5 is greater than 4!")  ; then-part
  (message "4 is not greater than 5!")) ; else-part

異なるレベルのインデントをすることで、then-part と else-part の区別がし やすくなることに注意しよう。(GNU Emacs は if 式を自動的に正しくイ ンデントするコマンドを幾つか備えている。GNU Emacs によるリストのタイプの支援, を参照のこと。)

type-of-animalif 式に新しく else-part の部分を追加する だけで、この関数を拡張することが出来る。

次のバージョンの type-of-animal の関数定義を評価してインストール すれば、その後の、異なる引数を渡すような二つのS式を評価して、結果を見る ことも出来るようになる。

 
(defun type-of-animal (characteristic) ; 二番目のバージョン
  "Print message in echo area depending on CHARACTERISTIC.  If the
CHARACTERISTIC is the symbol `fierce', then warn of a tiger; else say
it's not fierce."
  (if (equal characteristic 'fierce)
      (message "It's a tiger!")
    (message "It's not fierce!")))

 
(type-of-animal 'fierce)

(type-of-animal 'zebra)

(type-of-animal 'fierce) を評価すると、エコー領域には "It's a tiger" と表示されるはずだ。一方、(type-of-animal 'zebra) を評 価すると、"It's not fierce!" と表示される。

(勿論、characteristicferocious (獰猛) であったなら、 "It's not fierce!" と表示されるが、これは間違いである。コードを書 く場合は、if によってこのような引数がテストされる可能性を考慮に入 れて、それに応じてプログラムを書かなければならない。)


3.9 Lisp における真と偽

if 式の中の真偽テストについては、触れておかなければならない重要な 側面がある。今までは、真とか偽とかいう言葉を、述語の値としてあたかも新し い Lisp のオブジェクトとして話してきた。しかし実際は「偽」というのは単に 我々が親しんできた良き友 nil なのである。そして、その他は全て--- どんなものであっても---「真」である。

真であるか否かテストされるS式は、評価した結果が nil 以外の値であ れば true と判断される。別の言い方をすると、テストの結果は返された 値が47のような数値や、"hello" というような文字列、あるいは flowers というような (nil 以外の) シンボルや、リストであっ たり、はたまたバッファであったりする場合でも真であると解釈されるわけであ る。

このことを説明する前に、nil について説明せねばならないだろう。

Lisp ではシンボル nil は二つの意味を持つ。一つ目は空リストである。 そして二つ目は偽であり、真偽テストの結果が間違いであった場合に返る値であ る。nil は空リストとして () のように書いても良いし、 nil と書いてもよい。Lisp にとっては、()nil は全 く同じものである。しかし、人間にとっては nil は偽を表わすのに使い、 () は空リストを表わすのに使うのが普通だろう。

Lisp では nil以外---即ち空リスト以外---の全ての値は真と解釈される。 これは、評価した時に空リスト以外の何かを返すものは、if 式のテスト で真と判断されることを意味する。例えば、もしテストの部分に数値を置いたと すると、Lisp では数値は評価された時には自分自身を返すように出来ているの だから、結果としてこの場合の if 式でのテストは真となる。テストの 結果が偽となるのはS式を評価して nil、即ち、空リストが返る時だけ である。

次に挙げる二つのS式を評価してみることで、このことを確かめることが出来る。

最初の例では、if 式のテストの結果、数字の4が評価され、その数自身 が返される。結果として then-part が評価され、エコー領域に `true' が 表示される。二番目の例としては、nil は偽を表わすので 結果として else-part が評価され、エコー領域に `false' と表示される。

 
(if 4
    'true
  'false)

(if nil
    'true
  'false)

ついでながら、もしテストで真を返すような適当な値が見つからなければ、Lisp インタプリタは真を表わすためにシンボル t を返す。例えば、(> 5 4) は評価された場合 t を返す。これもいつものようにして確認出来 る。

 
(> 5 4)

一方、この関数は偽である場合は nil を返す。

 
(> 4 5)


3.10 save-excursion

関数 save-excursion は、この章で説明する4番目の、そして最後の特殊 形式である。

編集用の Emacs Lisp プログラムでは、関数 save-excursion は大変よ く使用されるものである。これは、ポイントとマークの位置をセーブし、関数の 本体を実行し、そしてその際にポイントとマークの位置が変わったなら、それを 元の位置に戻す。ポイントとマークが意外な場所に動いて、驚いたり、困惑した りするのを防ぐことがこの関数の主な目的である。

しかしながら、save-excursion について議論する前に GNU Emacs にお いて、ポイントとマークがどういうものであるかを復習しておくのが良いだろう。 Point は現在のカーソルの位置である。カーソルが何処にあろうと、それ がポイントである。より正確に言うと、カーソルが文字の上にあるような端末で は、ポイントは、その文字の左端の位置にある。Emacs Lisp ではポイントは整 数である。バッファの最初の文字は1であり、二番目の文字は2であり... といっ た感じだ。関数 point は現在の位置を数として返す。各々のバッファが それ自身のポイントの位置を持っている。

(訳註:より具体的に言うと、カーソルがバッファの先頭の文字の上にある場合 には、ポイントの値は1になることになる。また、二文字目以降は、日本語等の 場合は事情が変わる。例えば Mule では2バイトコードは3バイトで表現されるの で、ポイントの数は一文字あたり3ずつずれる勘定になる。)

mark は、ポイントとはまた別の、バッファにおけるある位置を指し示す ものである。その値は C-SPC (set-mark-command) で設定 される。マークが設定されている場合、C-x C-x (exchange-point-and-mark) を使ってカーソルをマークにジャンプさせ、 以前ポイントがあった場所にマークを設定することが出来る。更に、もし他のマー クが既にあった場合、以前のマークはマークリングに保存される。多くのマーク をこうして保存することが出来る。C-u C-SPC とタイプすることで、 以前保存されたマークに順にさかのぼって行くことが出来る。

バッファの中で、ポイントとマークに挟まれた部分を リージョン (region) と呼ぶ。リージョンに関するコマンドは沢山ある。例えば、 center-regioncount-lines-regionkill-region、そ して print-region なんかがそうである。

特殊形式 save-excursion はポイントとマークの位置を保存し、この特 殊形式内の本体部分のコードが Emacs Lisp によって評価された後に、その位置 を復元する。 こうして、例えばポイントがあるテキストの先頭にあり、あるコー ドがその位置をバッファの最後に移動してしまったとしても、本体部分のS式が 評価され終わった後に save-excursion がポイントを元の位置に戻して くれる。

Emacs ではユーザーが予期しない所でも、しばしば内部的にポイントの位置を動 かしていることがある。例えば count-lines-region なんかもポイント の位置を移動している。予期しない、また(ユーザーの立場からみて)不必要な移 動によってユーザーが煩わされるの防ぐために、save-excursion は頻繁 に使われる。save-excursion を使うのは家の中を住み心地の良いものに しておくことと一緒である。

家の中がきちんと整理されているかどうかを確認するために、 save-excursion は、たとえ、その内部のコードがうまく動かない場合に も(専門用語を使ってより正確に言うと、「異常終了した場合にも」)、ちゃんと ポイントとマークの位置を復元してくれる。この機能は大変役に立つ。

save-excursion は単にポイントとマークの値を記録するだけでなく、カ ントバッファも保持していて、それを復元してくれる。これは、バッファを変更 するようなコードを書いても、save-excursion が元のバッファに戻して くれることを意味している。実際、append-to-buffer の中では、 save-excursion はその目的で使われている。(append-to-buffer の定義, 参照。)

3.10.1 save-excursion 式のテンプレート  埋めるべきスロットは一つだけ


3.10.1 save-excursion 式のテンプレート

save-excursion のテンプレートは極めて単純である。

 
(save-excursion
  本体...)

関数の本体は一つないしは複数のS式であり、それらが順に Lisp インタプリタ によって評価されていく。もし本体に二つ以上のS式があったとすると、 save-excursion という関数自体の値としては、それらの内最後のS式の 値が返される。他のS式は単に副作用として評価されるのである。 save-excursion 自体は (ポイントとマークを復元するという) 副作用だ けのために用いられる。

より詳しく書くと、save-excursion 式のテンプレートは次のように書け る。

 
(save-excursion
  本体の一番目の式
  本体の二番目の式
  本体の三番目の式
   ...
  本体の最後の式)

ここでS式は、シンボルそのものか、もしくはリストである。

Emacs Lisp のコードでは、save-excursion 式は、let 式の中で よく使われる。次のような感じだ。

 
(let 変数リスト
  (save-excursion
    本体...))


3.11 復習

ここまでの幾つかの章で、かなりの数の関数と特殊形式を紹介してきた。ここで それらを簡単にまとめておくと同時に、そこでは触れなかった類似の関数なんか も紹介しておくことにする。

eval-last-sexp
現在のポイントの位置より前方の最後のS式を評価する。通常はS式が返す値は エコー領域に表示されるが、引数つきで呼び出された時だけはカレントバッファ に表示する。普通は、C-x C-e にバインドされている。

defun
関数を定義する。この特殊形式は最大5つの部分からなる。関数の名前、関数に 渡される引数のリスト、説明文字列、オプションのインタラクティブ宣言、そし て関数定義の本体である。

例)

 
(defun back-to-indentation ()
  "Point to first visible character on line."
  (interactive)
  (beginning-of-line 1)
  (skip-chars-forward " \t"))

interactive
インタプリタに対してその関数がインタラクティブに使用出来ることを宣言する。 この特殊形式の後に関数に渡す引数に関する情報として複数の部分からなる文字 列が続くことがある。これらの部分の中にはインタプリタに対してその情報を求 めるプロンプトを出すよう要求するものもある。この文字列の中では各々の部分 が改行コード `\n' によって区切られている。

よく使われるコード文字には、次のようなものがある。

b
現在存在するバッファ。

f
現在存在するファイル。

p
数値である 前置引数。(`p' が小文字であることに注意。)

r
二つの数値引数としてのポイントとマークを、小さい方を先に並べたもの。この コード文字のみが二つの引数の列を特定する。(他は一つだけ。)

コード文字の完全なリストについては section `Code Characters for `interactive'' in The GNU Emacs Lisp Reference Manual, を参照。

let
変数リストが let の本体部分のコードのみに対して使われることを宣言 し、かつ、それらの変数に nil ないしは指定した初期値を与える。そし て、let 式の本体部分にあるS式を評価し、その中の最後のS式を評価 した値を返す。let 式の本体の内部では、Lisp インタプリタはその let 式の外部にある同じ名前の変数は見ない。

例)

 
(let ((foo (buffer-name))
      (bar (buffer-size)))
  (message
   "This buffer is %s and has %d characters."
   foo bar))

save-excursion
この特殊形式の本体部分を評価する直前のポイントとマークの位置、及びカレン トバッファを保存する。そして本体実行後、ポイントとマークの位置及びバッファ を復元する。

例)

 
(message "We are %d characters into this buffer."
         (- (point)
            (save-excursion
              (goto-char (point-min)) (point))))

if
この関数の最初の引数を評価する。そして、もしその値が真であれば、二番目の 引数を評価し、そうでない場合、三番目の引数があればそれを評価する。

特殊形式 if条件分岐 (conditional) と呼ばれる。 Emacs Lisp には他にも条件分岐があるが、if はその中でも多分 もっともよく使われるものであろう。

例)

 
(if (string= (int-to-string 19)
             (substring (emacs-version) 10 12))
    (message "This is version 19 Emacs")
  (message "This is not version 19 Emacs"))

equal
eq
二つのオブジェクトが同じであるかどうかをテストする。equal の方は、 両者が同じ構造と中身を持ちさえすれば、真を返す。一方、eq の方は、 引数が両方とも実際に同じオブジェクトである時のみ真を返す。

<
>
<=
>=
関数 < は、最初の引数が二番目の引数よりも小さいかどうかをテストす る。これと対をなす関数 > は、最初の引数が二番目よりも大きいかどう かをテストする。同様に、<= は最初の引数が二番目の引数以下であるか どうかを、>= は最初の引数が二番目の引数以上であるかどうかをテスト する。どの場合でも引数は両方とも数値でなければいけない。

message
エコー領域にメッセージを表示する。このメッセージは一行のみでなければなら ない。最初の引数は `%s', `%d', あるいは `%c' を中に含む文 字列であり、これらはその後に続く引数におきかえられる。`%s' の部分に 入る引数は文字列かシンボルでなければならない。また、`%d' の部分に入 る引数は、数でなければならない。`%c' の所に入る引数も数でなければな らないが、こちらはそれに対応する ascii コードの文字が表示される。

setq
set
関数 setq は最初の引数に二番目の引数の値をセットする。最初の引数 は setq により、自動的に引用符が付けられる。続けて引数のペアを書 いた場合、それらについても同じことをする。もう一方の関数 set の方 は、二つの引数しか取ることが出来ない。また、その引数は両方ともまず最初に 評価され、その後、二番目の引数を評価して返された値を最初の引数を評価して 返された値にセットする。

buffer-name
引数は持たず、バッファ名前を文字列として返す。

buffer-file-name
引数は持たず、バッファがビジットしているファイルの名前を返す。

current-buffer
Emacs がアクティブであるようなバッファの中身を返す。これはスクリーンに見 えているバッファとは限らない。

other-buffer
(other-buffer に引数として渡したバッファ及び、カレントバッファを 除いて) もっとも最近選択したバッファの中身を返す。

switch-to-buffer
Emacs にとってアクティブなバッファを選択し、カレントウィンドウに表示して ユーザーが見えるようにする。普通は C-x b にバインドされている。

set-buffer
これからプログラムを走らせるバッファに Emacs を切り替える。ただし、表示 しているウィンドウはそのままである。

buffer-size
カレントバッファの文字数を返す。

point
現在のカーソル位置の値をそのバッファの最初の位置からの文字数として返す。

point-min
カレントバッファの中で許される最小のポイントの値を返す。ナローイングが有 効でない場合は1である。

point-max
カレントバッファの中で許される最大のポイントの値を返す。ナローイングが有 効でない場合はバッファの最後の位置に一致する。


3.12 練習問題


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

This document was generated by Matsuda Shigeki on April, 10 2002 using texi2html