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

4. バッファに関する幾つかの関数

この章では、GNU Emacs で使われている関数を、幾つか詳しく見てみることにす る。こういうことは、"walk-through" と呼ばれる。これらの関数は Lisp コー ドの例として扱うのであるが、最初の単純化された例を除いて、決して「机上」 のものではない。実際に GNU Emacs で使われている現実のコードである。これ らの関数定義から多くのことを学ぶことが出来る。この章で説明する関数は、皆 バッファに関連したものである。他のものについては後で学ぶことにしよう。

4.1 情報の探し方  
4.2 簡略版 beginning-of-buffer の定義  goto-char, point-min,
                                そして push-mark を見てみよう
4.3 mark-whole-buffer の定義  これは beginning-of-buffer と殆ど同じ
4.4 append-to-buffer の定義  これは save-excursion
                                insert-buffer-substring の利用している。
4.5 復習  
4.6 練習問題  


4.1 情報の探し方

この walk-through の中では、新しく出て来た関数はその都度、時には細かく、 時には簡単に、説明することにする。出て来た関数に興味を持った場合は、 C-h f に続けてその関数名をタイプして RET を押せば、どんな Emacs Lisp 関数についても何時でも、完全な説明文を見ることが出来る。同様 に変数についても C-h v に続けてその変数名(と RET) を押せば、 その変数の完全な説明文を見ることが出来る。

また、もしその関数の元のソースを見たくなったら、関数 find-tag を 使ってその関数に飛ぶことが出来る。まず M-. とタイプし (即ち、 META キーとピリオドキーを同時に押すか、または ESC キーを押し てからピリオドキーを押し), 出てきたプロンプトに対してソースコードを見た い関数の名前、例えば mark-whole-buffer 等、をタイプする。すると、 Emacs はバッファを切り替えて、その関数のソースコードをスクリーンに表示し てくれる。元のバッファに戻るには、C-x b RET とタイプすれば良 い。

あなたの使っている Emacs のデフォルトの初期値いかんでは `タグテーブル' を指定する必要があるかもしれない。これは `TAGS' と呼ばれるファイル である。`emacs/src' ディレクトリにあるファイルを指定することになる 場合が殆どであろう。その場合 M-x visit-tags-table コマンドを使っ て、

`/usr/local/lib/emacs/19.23/src/TAGS'

などのようにパス名を指定する。section `Tag Tables' in The GNU Emacs Manual, 参照。 また、自分自身のものを作成する方法については 自分自身の `TAGS' ファイルの作成, を見よ。

Emacs Lisp に慣れてしまってからは、ソースコードを眺めるために頻繁に find-tag を使うことになるだろう。そして自分自身の `TAGS' テー ブルを作ることになるはずだ。

ついでに付け加えておくと、Lisp コードを含むファイルは便宜上ライブラリ (library) と呼ばれる。このメタファは、一般的なライブラリではなく、 例えば法律とか工学とかのような特定分野のライブラリから来ている。各々の ライブラリないしはファイルには特定のトピックや動作に関する関数が含まれて いる。例えば、`abbrev.el' には省略等のショートカットキーを扱う関数 が、また `help.el' にはオンラインヘルプを扱う関数が収められている、 といった感じである。(時には複数のライブラリが一つの動作に関するコードを 提供していることもある。電子メールを読むためのコードを提供する `rmail...' ファイルなんかがそうだ。) The GNU Emacs Manual を見れば、「C-h p コマンドによって標準的な Emacs Lisp ライ ブラリをトピックをキーワードにして検索することが出来る」と言った記述を 見つけることが出来る。


4.2 簡略版 beginning-of-buffer の定義

まず beginning-of-buffer という関数から始めるのが良いであろう。こ の関数には既に慣れているだろうし、理解するのも易しいからだ。インタラクティ ブなコマンドとして使われるものであろうと、beginning-of-buffer は カーソルをバッファの先頭に移動させ、以前の位置にマークを置く。この関数は 大抵は M-< にバインドされている。

この章では、この関数の短縮されたバージョンを説明することで、これがどうい うふうに使われることが多いのかを示そう。この短縮版も上に書いたような動作 をするが複雑なオプションは付いていない。完全版については別のセクションで 説明することにする。(beginning-of-buffer の完全な定義, 参照。)

コードを見る前に、どんな関数定義が含まれているかを考えてみよう。まず、関 数をインタラクティブにするための、即ち M-x beginning-of-buffer と タイプしたり、あるいは M-< といったキーコードをタイプすることで呼 び出せるようにするためのS式を含んでいなければならない。また、バッファの 元の位置にマークするためのコードも必要だ。更にカーソルをバッファの先頭に 移動するコードも入っていなくてはならない。

これがこの関数の短縮版の完全なテキストである。

 
(defun simplified-beginning-of-buffer ()
  "Move point to the beginning of the buffer; 
leave mark at previous position."
  (interactive)
  (push-mark)
  (goto-char (point-min)))

他の関数定義と同様に、この定義も特殊形式 defun に続く五つの部 分からなっている。

  1. 名前: この場合は simplified-beginning-of-buffer

  2. 引数リスト: この場合は空リスト ()

  3. 説明文字列

  4. インタラクティブにするためのS式

  5. 本体

この関数定義では、引数リストは空である。これは、この関数が引数を必要とし ないことを意味する。(もっとも完全版の方の定義を見れば、そちらにはオプショ ンの引数を渡すことが出来ることが分る。)

インタラクティブにするためのS式は、Emacs にこの関数がインタラクティブに 使われることを伝えるための物である。今の場合 interactive は引数を 持たない。というのも simplified-beginning-of-buffer は引数を必要 としないからである。

関数の本体部分は次の二行からなる。

 
(push-mark)
(goto-char (point-min))

一行目は (push-mark) というS式である。このS式が Lisp インタプリ タによって評価されると、カーソルが何処にあろうと、その位置にマークが設定 される。マークの位置はマークリングに保存される。

二行目は (goto-char (point-min)) である。このS式はカーソルをバッ ファ内での最小ポイント、即ちバッファの先頭に移動する。(あるいは、もし ナローイングがかかっていれば、アクセス出来る範囲内での最初の位置に移動す る。ナローイングとワイドニング, 参照。)

push-mark コマンドによって、(goto-char (point-min)) でカー ソルがバッファの先頭に飛ぶ前に、そのカーソルの位置にマークがセットされる。 結果として、もし元の位置に戻ろうと思えば、C-x C-x によって元の位置 に戻ることが出来る。

これがこの関数定義の全てである!

もし、このようなコードを読んでいて goto-char のようによく知らない 関数に出くわしたとする。その場合には describe-function コマンドを 使ってこれがどんな関数かを見ることが出来る。このコマンドを使うには、まず C-h f とタイプし、次に知りたい関数の名前をタイプしてから RET を押せば良い。describe-function コマンドは `*Help*' ウィンド ウにその関数の説明文字列を表示してくれる。例えば goto-char の説明 文は次の通りである。

 
One arg, a number.  Set point to that number.
Beginning of buffer is position (point-min),
end is (point-max).

(describe-function はプロンプトを出す時にデフォルトの値としてその 時のカーソルの直前にあるシンボルの値を設定する。従って、カーソルをその関 数の直後に持っていって C-h f RET とタイプすることも出来る。)

end-of-buffer 関数についても beginning-of-buffer とほぼ同 じである。ただし、関数の本体部分で、(goto-char (point-min)) の部 分が (goto-char (point-max)) となっている。


4.3 mark-whole-buffer の定義

関数 mark-whole-buffer も、関数 simplified-beginning-of-buffer と同じくらい簡単に理解出来る。 ただし、今回は単純化したものではなく完全な関数を見ることにする。

mark-whole-buffer 関数は beginning-of-buffer 関数ほどには 頻繁には使われない。しかし、同じくらい有用である。この関数はポイントをバッ ファの先頭に、マークをバッファの最後に置くことで、バッファ全体をリージョ ンとして指定する。また、大抵は C-x h にバインドされている。

この関数の完全なコードは次の通りである。

 
(defun mark-whole-buffer ()
  "Put point at beginning and mark at end of buffer."
  (interactive)
  (push-mark (point))
  (push-mark (point-max))
  (goto-char (point-min)))

他の全ての関数と同様、mark-whole-buffer も関数定義のテンプレート にきちんと当てはまっている。このテンプレートは次のようなものだった。

 
(defun 関数名 (引数リスト)
  "説明文字列..."
  (インタラクティブ式...)
  本体...)

この関数の動作であるが、関数名は mark-whole-buffer である。引数リ ストが空リスト `()' になっているので、この関数は引数を必要としない ことが分る。次に説明文字列が来ている。

次の行は (interactive) である。これをつけると Emacs はこの関数を インタラクティブに使えるものとみなす。ここら辺のことは前節の simplified-beginning-of-buffer 関数の所でも説明した通りである。

4.3.1 mark-whole-buffer の本体  本体は三行しかない


4.3.1 mark-whole-buffer の本体

関数 mark-whole-buffer の本体は次の三行からなる。

 
(push-mark (point))
(push-mark (point-max))
(goto-char (point-min))

この三行のうち最初の行はS式 (push-mark (point)) である。

この行は関数 simplified-beginning-of-buffer の本体の最初の行の (push-mark) と全く同じ働きをする。どちらの場合にも Lisp インタプ リタはカーソルの現在の位置にマークを設定する。

私には何故 mark-whole-buffer 関数では (push-mark (point)) と書かれ、beginning-of-buffer 関数では (push-mark) と書か れているか分らない。多分、このコードを書いた人は (push-mark) の引 数は省略可能で、引数を受け取らない場合、デフォルトでは自動的に現在のポイ ントの位置にマークが設定されるということを知らなかったんだろうと思う。あ るいはこのS式が次の行と同じような構造になるように書いたのかもしれない。 いずれにしてもこの行によって Emacs はポイントの位置を決定し、その位置に マークを設定する。

mark-whole-buffer の次の行は (push-mark (point-max)) であ る。このS式は、バッファの中でポイントの数が最大の位置にマークを設定する。 これは大抵はバッファの最後である。(もし、バッファがナローイングされてい れば、バッファの中のアクセス可能な範囲での最後尾になる。ナローイングにつ いての詳細は ナローイングとワイドニング, 参照。) このマークが設定された時点で、現在のポイントの位置に設定されてい た、以前のマークは解除される。しかし、Emacs は最近設定されたマークの位置 と同様、その位置を記憶している。もし戻りたければ、C-u C-SPC を二度タイプすることで、その位置に戻ることが出来る。

さて、いよいよ最後の行の (goto-char (point-min)) である。これは beginning-of-buffer と全く同じように書かれている。このS式はカー ソルをバッファの中でポイントが最小の位置に移動させる。つまり、バッファの 先頭(もしくはアクセス可能な範囲での先頭)に移動させるのである。その結果、 ポイントはバッファの先頭に移動し、バッファの最後尾にマークが設定される。 このようにして、バッファ全体がリージョンになる。


4.4 append-to-buffer の定義

append-to-buffer コマンドも mark-whole-buffer コマンドと同 じくらい単純な構造をしている。このコマンドがすることはカレントバッファの リージョン(つまり、バッファ中のポイントとマークある部分) を指定したバッ ファに複写することである。

append-to-buffer コマンドは、リージョンを複写するのに関数 insert-buffer-substring を使う。この関数は名前から察せられる通り、 バッファのある部分から文字からなる文字列、即ち「部分文字列」を取り出して、 それを他のバッファに挿入する。関数 append-to-buffer の大部分は insert-buffer-substring がうまく動作するような状態に設定すること に関するものである。このコードはテキストが写されるバッファとそのコピー元 となるリージョンの両方の指定が必要となる。この関数の完全なコードは次の通 りである。

 
(defun append-to-buffer (buffer start end)
  "Append to specified buffer the text of the region.
It is inserted into that buffer before its point.

When calling from a program, give three arguments:
a buffer or the name of one, and two character numbers
specifying the portion of the current buffer to be copied."
  (interactive "BAppend to buffer: \nr")
  (let ((oldbuf (current-buffer)))
    (save-excursion
      (set-buffer (get-buffer-create buffer))
      (insert-buffer-substring oldbuf start end))))

この関数は一連のテンプレートが埋められたものと見倣すと理解しやすい。

最も外側のテンプレートは、関数定義のものである。今の場合、次のようになっ ている(幾つか既に埋めてあるスロットもある)。

 
(defun append-to-buffer (buffer start end)
  "説明文字列..."
  (interactive "BAppend to buffer: \nr")
  本体...)

最初の一行は関数の名前とその三つの引数を含んでいる。引数はテキストが複写 される行き先のバッファ及び、複写元のリージョンを指定する startend である。

関数の次の部分には簡潔で完全な説明が記されている。

4.4.1 インタラクティブ式 append-to-buffer  二つの部分からなるインタラクティブ式
4.4.2 append-to-buffer の本体  let 式との組み合わせ
4.4.3 append-to-buffer の中の save-excursion  save-excursion はどう働くか


4.4.1 インタラクティブ式 append-to-buffer

append-to-buffer という関数はインタラクティブに使われる関数なので、 interactive 式が必要である。(interactive について復習する には、関数をインタラクティブにする, を見よ。)

 
(interactive "BAppend to buffer: \nr")

このS式は二重引用符に囲まれた引数を持ち、その引数は `\n' によって 分けられた二つの部分からなっている。

一つ目の部分は `BAppend to buffer:' である。ここで、`B' は Emacs に対し、この関数に渡すバッファ名を尋ねるように要求している。これに よって Emacs はミニバッファに `B' に続く文字列---今の場合は `Append to buffer: '---からなるプロンプトを出して、ユ-ザーに名前 を入力するよう促す。Emacs はこの関数の引数リストにある変数 buffer に、そこで指定されたバッファをバインドするのである。

改行コード `\n' は引数の最初の部分と二つ目の部分を分けるために用 いられる。`\n' の次には `r' が続いているが、これは関数の引数リ ストの中の buffer に続く二つのシンボル (つまり、startend) にポイントとマークの値をバインドすることを示している。


4.4.2 append-to-buffer の本体

関数 append-to-buffer の本体部分は let から始まっている。

以前見たように (let, を参照) let 式の目的は、 let 式内部だけで使われる変数を一つないしは複数作り、それに初期値 を与えることである。これは、そのような変数は let 式外部の同じ名前 の変数と混同されることがない、ということを意味している。

let 式の部分をアウトラインにして append-to-buffer のテンプ レートを書いてみる。これによって let 式がどういうふうに関数に組み込 まれているかが分る。

 
(defun append-to-buffer (buffer start end)
  "説明文字列..."
  (interactive "BAppend to buffer: \nr")
  (let ((変数 ))
        本体...)

let 式は三つの要素を持っている。

  1. シンボル let

  2. 変数リストの中身。今の場合は二つの要素からなるリスト (variable value) が一つあるだけ。

  3. let 式の本体。

関数 append-to-buffer の中では変数リストは次のようになっている。

 
(oldbuf (current-buffer))

let 式のこの部分では、変数 oldbuf(current-buffer) を評価して返された値にバインドされる。変数 oldbuf は現在作業しているバッファの記録のために使われるものである。

変数リストの要素は一組の括弧に囲まれている。これによって Lisp インタプリ タは変数リストと let 式の本体を区別出来るのである。その結果として 変数リストの中の、二つの要素からなるリストであるような要素は更に一組の括 弧によって囲まれることになる。ということで、この行は次のようになる。

 
(let ((oldbuf (current-buffer)))
  ... )

ひょっとすると oldbuf の前に二つの括弧があることに驚くかもしれな いが、oldbuf の前の最初の括弧は変数リストの区切りを示すものであり、 次の括弧は二つの要素からなるリスト (oldbuf (current-buffer)) の最 初の括弧であることさえ分れば問題ないだろう。


4.4.3 append-to-buffer の中の save-excursion

append-to-buffer の中の let 式の本体は save-excursion 式からなっている。

関数 save-excursion はポイントとマークの位置を保存し、 save-excursion の本体部分のS式の実行が完了した時点でそれらを元の 位置に戻す。更に save-excursion は元々のバッファが何であったかも 憶えていて、そのバッファに戻してくれる。save-excursionappend-to-buffer の中でこのように使われている。

ついでだが、ここで Lisp の関数の書式としては、通常複数の行に広がって括弧 で閉じられているようなものは全て、最初のシンボルよりも深くインデントされ ていることに注意しておいた方が良いだろう。今回の関数定義の中では次のよう に let 式は defun よりも深くインデントされている。

 
(defun ...
  ...
  ...
  (let...
    (save-excursion
      ...

このように書式を工夫することで、save-excursion の本体部分の二行が save-excursion に付随する括弧で囲まれていることが簡単に見てとれる。 同様に、save-excursion それ自身が let に付随する括弧で囲ま れていることもすぐに分る。

 
(let ((oldbuf (current-buffer)))
  (save-excursion
    (set-buffer (get-buffer-create buffer))
    (insert-buffer-substring oldbuf start end))))

関数 save-excursion の使い方は、次のテンプレートのスロットが埋め られているものだと思うとよく理解出来るだろう。

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

この関数では save-excursion には二つのS式しか含まれていない。そ の本体は次の通りである。

 
(set-buffer (get-buffer-create buffer))
(insert-buffer-substring oldbuf start end)

関数 append-to-buffer が評価されると、save-excursion の本体 部分の二つのS式が順に評価される。最後のS式の値が save-excursion 関数の値として返される。もう一方のS式は単に副作用だけのために評価される。

save-excursion の本体の最初の行は、現在のバッファを append-to-buffer の引数の最初の引数で指定されたものに切り替えるた めに関数 set-buffer を使っている。(バッファの切替は副作用である。 前にも言ったように Lisp にとっての副作用こそが我々の主目的であることが多 い。) 二行目がこの関数の本来の作業を行う。

関数 set-buffer は Emacs の注意をテキストをコピーしようとしている バッファの方に向けさせる。そのバッファから save-excursion によっ て元のバッファに帰ってくるのである。

 
(set-buffer (get-buffer-create buffer))

このリストのもっとも内側にあるS式は (get-buffer-create buffer) というものである。このS式は get-buffer-create 関数を使っている。 これはその名前のバッファの内容を取り込むか、もし存在しない場合は新しくそ の名前を持つバッファを作成する。これは append-to-buffer 関数を使 うことで、それ以前には存在してなかったバッファにもテキストを出力すること が出来ることを示している。

get-buffer-create はまた set-buffer が不必要なエラーに遭遇 するのを防いでもいる。つまり set-buffer を使う時は行き先のバッファ が存在している必要があるのだ。もしありもしないバッファを指定したりすると Emacs はそこで仕事をやめてしまう。存在しないバッファである場合には get-buffer-create がそのバッファを作ってくれるので、 set-buffer は常にバッファを得ることが出来るというわけである。

append-to-buffer の最後の行はテキストを追加する働きをする。

 
(insert-buffer-substring oldbuf start end)

関数 insert-buffer-substring は最初の引数で指定されたバッファ から文字列をコピーし、それを現在のバッファに挿入する。今の場合は insert-buffer-substring の引数は let で生成されバインドさ れた変数 oldbuf の値であり、これは append-to-buffer コマン ドを実行した時点でのカレントバッファである。

insert-buffer-substring が作業を終えると、save-excursion が元のバッファに戻してくれる。そうして append-to-buffer の仕事は 完了する。

骨組みだけ書くと、本体部分がやっていることは次の通りである。

 
(let (oldbufcurrent-bufferの値にバインド)
  (save-excursion                       ; バッファの状態の保存
    バッファ切替
    現在のバッファにoldbufから部分文字列を挿入)

  終わったら元のバッファに戻る
終了後はoldbufの局所的な意味は消え去る

以上まとめると、append-to-buffer は次のような働きをする。まずはカ レントバッファの値を oldbuf という変数に保存する。次に別のバッファ をユーザーから聞き出し、もし必要なら新規に作成して、そのバッファに移る。 oldbuf の値を利用して元のバッファのリージョン内のテキストを取り出 し新しいバッファに挿入する。そして最後に save-excursion を用いて 元のバッファに戻る。

append-to-buffer を見ていく中で、かなり複雑な関数を探検したことと 思う。letsave-excursion の使い方も見られたし、複数のバッ ファ間の行き来の仕方も見た。他の多くの関数定義の中でもこのようにして let, save-excursion, そして set-buffer を利用している。


4.5 復習

ここで、この章で議論した様々な関数について簡単にまとめておく。

describe-function
describe-variable
関数ないしは変数の説明文字列を表示する。通常は、C-h fC-h v にバインドされている。

find-tag
関数のソースや変数を含むファイルを探して、そのバッファに移り、その関数や 変数が定義されている位置にポイントを移動する。通常は M-. にバイン ドされている。(これは、META キーを押して終止符を押すという意味で ある。)

save-excursion
ポイントとマークの位置を保存し、save-excursion の引数が評価された 後で、それらの値を元に戻す。また、カレントバッファも憶えていて、そのバッ ファに戻してくれる。

push-mark
マークを現在の位置に設定し、直前のマークの値を、マークリングに記録する。 このマークは、バッファ内の位置を示し、たとえそのバッファにテキストが追加 されたり削除されたりしても、その相対的な位置を保持してくれる。

goto-char
ポイントを引数の値で指定された位置に移動する。引数は数値あるいはマーカ、も しくは (point-min) のように位置を表わす数値を返すようなS式でなけれ ばならない。

insert-buffer-substring
この関数に引数として渡されたバッファのリージョンを現在のバッファに挿入す る。

mark-whole-buffer
バッファ全体をリージョンに設定する。普通は C-x h にバインドされて いる。

set-buffer
Emacs が注目しているバッファを他のバッファに切り替える。が、ディスプレイ しているウィンドウは変更しない。人間が他のバッファで作業したい時よりかは プログラムの中でよく使われるものである。

get-buffer-create
get-buffer
ある名前を持つバッファを見つける。その名前のバッファが無い場合は新しく作 る。get-buffer はもしその名前のバッファが無い場合には nil を返す。


4.6 練習問題


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

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