[Prev] [Index] [Next] [上端へ移動] [下端へ移動].

2.評価の練習

更新日時:2008年05月07日

xyzzy Lispにおける関数定義の書き方を学ぶまえに、 これまでに説明してきたさまざまな式の評価に時間を割いてみるのも有益であろう。 リストの先頭(あるいは唯一)の要素が関数であるようなリストである。バッファに関する関数は、 単純でしかも興味深いので、これらから始めよう。本節では、それらのいくつかを評価してみる。 他の節では、バッファに関連した数個の別の関数のコードを調べて、それらがどのように書かれているかを見てみる。

How to Evaluate

xyzzy Lispに、カーソルの移動や画面上のスクロールなどの 編集コマンドを与えるたびに、 先頭要素が関数である式を評価している。 xyzzyはこのようにして動いている。

キーをタイプすると、Lispインタープリタに式を評価させることになり、そのようにして操作しているのである。 テキストをタイプした場合でさえも、xyzzy Lispの関数を評価しており、 タイプした文字を単に挿入する関数self-insert-commandを使った関数を評価しているのである。 キーをタイプすることで評価される関数は、対話的(interactive)関数とか コマンド(commands)と呼ばれる。関数を対話的にする方法については、 関数定義の書き方の章で例を示す。 See 節 3.3 関数を対話的にする。

キーボードコマンドをタイプする以外にも、式を評価する方法についてはすでに説明した。 すなわち、リストの直後にカーソルを置いてC-x C-eとタイプするのである。 本節の残りでは、この方法を用いる。これ以外にも式を評価する方法があり、必要に応じて他の節で説明する。

これからの数節で説明する関数は、評価を練習すること以外にも、それ自体、重要なものである。 これらの関数を学ぶことで、バッファとファイルの違い、バッファを切り替える方法、 バッファ内での位置を調べる方法が明らかになる。

[上端へ移動] [下端へ移動]

2.1バッファ名

2つの関数、buffer-nameget-buffer-file-nameが、 ファイルとバッファの違いを示してくれる。 buffer-nameは、どのバッファを返すかを、引数で指定しなければならない。 現在のバッファ名を返すにはselected-bufferを引数に指定すればよい。 式(buffer-name(selected-buffer))を評価すると、ステータスバーにバッファ名が表示される。 (get-buffer-file-name)を評価すると、バッファが参照するファイル名がステータスバーに表示される。 通常、(buffer-name)が返す名前は、そのバッファが参照するファイルの名前と同じであり、 (get-buffer-file-name)が返す名前はファイルのフルパス名である。

ファイルとバッファは異なる2つの実体である。ファイルは(削除しない限り) コンピュータに恒久的に記録された情報である。一方、バッファはxyzzyの内部にある情報であり、 (バッファを削除するか)編集作業を終了すると消えてしまう。通常、 バッファにはファイルからコピーした情報が収められている。これを、 バッファがファイルを訪れる(visiting)という。 このコピーを処理したり修正したりしているのである。バッファを変更しても保存しない限り、 ファイルは変更されない。バッファを保存すると、バッファはファイルにコピーされ、 その結果、恒久的に保存されるのである。

www-modeを使って読んでいる場合には、つぎのそれぞれの式の直後にカーソルを置いて C-x C-eとタイプすれば、それぞれの式を評価できる。

(buffer-name(selected-buffer))

(get-buffer-file-name)

筆者がこれを行うと、(buffer-name(selected-buffer))を評価して返される値は `" *WWW VIEW: http://homepage2.nifty.com/coin/xyzzy_lisp_prog/evaluate.html"'であり、 (get-buffer-file-name)を評価して返される値は `nil'である。`nil'と表示される理由は、www-modeで見ている場合ファイルが存在しなからである。 適当なファイルに(get-buffer-file-name)をコピーして、C-x C-e とタイプすれば、そのファイル名とフルパスがステータスバーに表示される。 (*scratch*バッファにはコピーしないように。*scratch*にはファイルがないため`nil'と表示される) 前者はバッファ名であり、後者はファイル名である(各式には括弧があるので、 Lispインタープリタはbuffer-nameget-buffer-file-nameを関数として扱う。括弧がないと、 インタープリタは変数としてシンボルを評価しようとする。 See 節 1.7 変 数。

ファイルとバッファの違いにもかかわらず、バッファを意味してファイルといったり、 その逆のいい方をする場合が多い。もちろん、ほとんどの人は、 「すぐにファイルに保存するバッファを編集している」というよりは、 「ファイルを編集している」という。その人が何を意図しているかは、ほとんどの場合、 文脈から明らかである。しかし、コンピュータプログラムに関していえば、 コンピュータは人間のように賢くはないので、つねに違いを心にとめておく必要がある。

ところで、用語「バッファ(buffer)」は、衝突力を弱めるクッションを意味する語に由来する。 初期のコンピュータでは、ファイルとコンピュータの中央処理装置のあいだの相互作用のクッションがバッファであった。 ファイルを格納するドラムやテープと中央処理装置とは、互いに大きく異なる装置であり、 それぞれ固有の動作速度で動いていた。バッファにより、これらが効率よく協調動作することが可能であった。 しだいに、バッファは、中間の一時的な保管場所から、実際の処理を行う場所へと進展していった。 これは、小さな港が大都市に発展したり、貨物を船に積み込むまえの一時的な保管倉庫がその重要性のために ビジネスや文化の中心に進展したことに似ている。

すべてのバッファがファイルに関連付けられるわけではない。たとえば、 ファイル名をいっさい指定せずにxyzzyを起動した場合には、画面にはバッファ`*scratch*'が表示されて xyzzyが動き出す。このバッファはいかなるファイルも訪問していない。同様に、 バッファ`*Help*'にはいかなるファイルも関連付けられていない。

バッファ`*scratch*'に切り替えてから、(buffer-name(selected-buffer))と入力して その直後にカーソルを置いてC-x C-eとタイプしてこの式を評価すると、 名前"*scratch*"が返されてステータスバーに表示される。"*scratch*"がバッファの名前である。 しかし、バッファ`*scratch*'で(get-buffer-file-name)とタイプしてこれを評価すると、 ステータスバーにはnilと表示される。nilの語源はラテン語の 「無(nothing)」を意味する単語であり、この場合、バッファ`*scratch*'にはいかなるファイルも 関連付けられていないことを意味する(Lispでは、nilは「偽(false)」をも意味し、空リスト()の同義語でもある)。

バッファ`*scratch*'を使っているときに、式の評価結果をステータスバーではなく バッファ`*scratch*'そのものに表示したい場合には、C-x C-eのかわりに C-u C-x C-eとタイプする。これにより、式の直後に返された値が表示される。 バッファはつぎのようになる。

(buffer-name(selected-buffer))
"*scratch*"

www-modeは読み出し専用なのでバッファの内容を変更できないため、 www-modeではこの方法を使えない。しかし、編集可能なバッファならば、この方法を使える。 コードや(本書のような)文書を書くときには、この機能はとても便利である。

[上端へ移動] [下端へ移動]

2.2 バッファの取得

関数buffer-nameは、バッファの名前を返す。バッファそのものを取得するには、 別の関数selected-bufferが必要である。この関数をコードで使うと、 バッファそのものを取得することになる。

名前とその名前が表すオブジェクトや実体とは互いに異なるものである。読者自身は読者の名前ではない。 読者は、その名前で他人が参照する「人」である。読者がGeorgeと話したいと頼んだときに、 `G'、`e'、`o'、`r'、 `g'、`e'と文字が書かれたカードを渡されたら、驚くであろうが、満足はしないであろう。 名前と話をしたいのではなくて、その名前で参照される人と話をしたいのである。バッファも同様である。 一時的バッファの名前は`*scratch*'であるが、名前そのものがバッファではない。 バッファそのものを得るにはselected-bufferのような関数を使う必要がある。

しかし、多少込み入った事情もある。ここで試すように、式でselected-bufferを評価すると バッファの内容ではなくバッファの表示形式が表示される。xyzzyがこのように動作する理由は2つある。 バッファには数千もの行が含まれることもあるので、これを簡便に表示するには長すぎる。 また、バッファの内容は同じであっても、名前が異なるバッファもありえるので、それらを区別できる必要がある。

例を示そう。

(selected-buffer)

いつものようにこの式を評価すると、ステータスバーには`#<buffer: カレントバッファの名前>'と表示される。 バッファ名ではなく、バッファそのものを表す特殊な表示形式である。

数やシンボルはプログラムに入力できるが、バッファの表示形式を入力することはできない。 バッファそのものを得る唯一の方法は、selected-bufferのような関数を使うことである。

関連する関数としてother-bufferがある。これは、現在使用しているバッファの直前に選択していた バッファを返す。たとえば、バッファ`*scratch*'から現在のバッファに切り替えた場合には、 other-bufferはバッファ`*scratch*'を返す。

つぎの式を評価してみよう。

(other-buffer)

ステータスバーには、`#<buffer: *scratch*>'、あるいは、今のバッファに切り替えるまえの バッファの表示形式が表示される

[上端へ移動] [下端へ移動]

2.3 バッファの切り替え

関数other-bufferの目的は、バッファそのものを引数に必要とする関数にバッファを与えることである。 別のバッファに切り替えるためother-bufferswitch-to-bufferとを使ってみよう。

まず、関数switch-to-bufferの概要を説明しておこう。 (buffer-name)を評価するために現在のバッファからバッファ`*scratch*'へ切り替えるときには、 C-x bとタイプし、ミニバッファに表示された切り替え先バッファ名のプロンプトに `*scratch*'と入力したであろう。C-x bとタイプすると、 Lispインタープリタはxyzzy Lispの対話的関数switch-to-bufferを評価する。 すでに説明したように、xyzzyはこのように動作する。キー列が異なれば、異なる関数を呼び出す、 つまり、実行するのである。たとえば、C-fとタイプするとforward-charを呼び出し、 M-fとタイプするとforward-wordを呼び出すなどである。

switch-to-bufferを書いた式に切り替え先のバッファを指定すれば、 C-x bと同じようにバッファを切り替えられる。

たとえば、つぎのLispの式である。
つぎの式はC-x C-eで評価してもother-bufferの名前がステータスバーに表示 されるだけである。実際にバッファを切り替えるにはESC ESCとタイプする。 ミニバッファに`Eval:'と表示されるので(switch-to-buffer (other-buffer))と打ち込み RETを押す。それでバッファを切り替えることができる。(元のバッファに戻るには C-x b RETと打ち込む)

(switch-to-buffer (other-buffer))

リストの先頭要素はシンボルswitch-to-bufferであるので、Lispインタープリタはこれを関数として扱い、 それに結び付けられている命令列を実行する。しかし、そのまえに、インタープリタは other-bufferが括弧の内側にあることを検出して、まずそのシンボルを処理する。 other-bufferはこのリストの先頭(かつ唯一の)要素なので、 Lispインタープリタはこの関数を呼び出す。これは別のバッファを返す。続いて、インタープリタは、 この別のバッファを引数としてswitch-to-bufferに渡して実行し、そのバッファへ切り替える。

本書の以降の節のプログラム例では、関数switch-to-bufferよりも 関数set-bufferを多用する。これは、コンピュータプログラムと人間との違いによるものである。 人間には目があるので、端末画面で作業中のバッファを見ることを期待する。これは当然のことであり、 これ以上説明する必要はなかろう。一方、プログラムに目はない。 コンピュータプログラムがバッファを処理するときに、端末画面でバッファが見えている必要はない。

switch-to-bufferは人間向けに考えられたものであり、異なる2つのことを行う。 xyzzyの注意をバッファに向けることと、ウィンドウに表示するバッファをそのバッファに切り替えることである。 一方、set-bufferは1つのことだけを行い、コンピュータプログラムの注意を そのバッファに向けるだけである。画面上のバッファは変更しない (もちろん、コマンドが終了するまでは、何も起こらない)。

ここでは、新たな専門用語呼び出し(call)も使った。 リストの先頭のシンボルが関数であるようなリストを評価すると、その関数を呼び出すのである。 この用語は、鉛管工を「呼ぶ」とパイプの洩れを修理してくれるのと同じように、 関数は、「呼び出す」と何かを行ってくれる実体であるという考え方からきている。

[上端へ移動] [下端へ移動]

2.4 バッファサイズとポイントの位置

最後に、buffer-sizepointpoint-minpoint-max などの比較的単純な関数を見てみよう。これらにより、バッファのサイズやバッファ内のポイントの 位置に関する情報を得ることができる。

関数buffer-sizeは、カレントバッファのサイズを返す。つまり、バッファ内の文字の個数を返す。

(buffer-size)

いつものように、この式の直後にカーソルを置いてC-x C-eとタイプすれば、この式を評価できる。

xyzzyでは、カーソルの現在位置をポイント(point)と呼ぶ。 式(point)は、バッファの先頭からポイントまでの文字の個数として、カーソルの位置を返す。

いつものようにつぎの式を評価すると、バッファ内でのポイントまでの文字数を調べることができる。

(point)

筆者の場合、この値は7166であった。本書の例では、関数pointを多用している。

ポイントの値は、当然であるが、バッファ内での位置に依存する。ここでつぎの式を評価すると、より大きな数になる。

(point)

筆者の場合、ポイントの値は7277となり、2つの式のあいだには(空白を含めて)111文字あることがわかる。

関数point-minは、pointとほぼ同じであるが、 カレントバッファにおいて取ることが可能なポイントの最小値を返す。 ナロイング(narrowing)していなければ、これは数0である(ナロイング(偏狭化)とは、 プログラムなどで操作するバッファの範囲を制限する機構である。同じように、 関数point-maxは、カレントバッファにおいて取ることが可能なポイントの最大値を返す。

[上端へ移動] [下端へ移動]

2.5 演習問題

適当なファイルを読み込み、その中ほどに移動する。バッファ名、ファイル名、 長さ、ファイル内での位置のそれぞれを調べてみよ。

[Prev] [Index] [Next] [上端へ移動] [下端へ移動]


このページはMeadow/Emacs memo(http://www.bookshelf.jp/)の Emacs Lisp プログラミングを改変したものです。
リンクはどのページでも自由にして下さい。
メールはkyupon@gmail.comまで送って下さい