[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5. 多少複雑な関数 (2004/08/05)

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=More%20Complex"
"intro2/多少複雑な関数"へのコメント(無し)

本章では、前章で学んだことをもとに、多少複雑な関数を見てみよう。 関数copy-to-bufferでは、1つの定義内で式save-excursionを 2つ使う方法を、関数insert-bufferでは、 interactive式でのアスタリスクの使い方とorの使い方を、 名前と名前が参照するオブジェクトとの重要な違いを示す。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5.1 copy-to-bufferの定義 (2004/08/05)

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=copy-to-buffer"
"intro2/copy-to-bufferの定義"へのコメント(無し)

append-to-bufferの動作を理解していれば、 copy-to-bufferを理解するのは容易である。 この関数はテキストをバッファにコピーするが、 指定したバッファに追加するのではなく、指定バッファの内容を書き換える。 関数copy-to-bufferのコードは、append-to-bufferのコードと ほぼ同じであるが、erase-bufferともう1つsave-excursionを使う (append-to-bufferの説明は、 See 節 4.4 append-to-bufferの定義 (2004/08/05))。

copy-to-bufferの本体はつぎのとおりである。

 
...
(interactive "BCopy to buffer: \nr")
  (let ((oldbuf (current-buffer)))
    (save-excursion
      (set-buffer (get-buffer-create buffer))
      (erase-buffer)
      (save-excursion
        (insert-buffer-substring oldbuf start end)))))

このコードは、append-to-bufferのコードとほぼ同じである。 append-to-bufferの定義と違いがでるのは、 コピー先のバッファに切り替えたあとである。 関数copy-to-bufferでは、バッファの既存の内容を削除する (つまり、書き換えるのである。 Emacsでは、テキストを書き換えるには、既存のテキストを削除してから、 新たなテキストを挿入する)。 バッファの既存の内容を削除したあと、 2つめのsave-excursionを使い、新たなテキストを挿入する。

なぜsave-excursionを2つ使うのだろうか?  関数の動作を見直してみよう。

copy-to-bufferの本体の概略はつぎのとおりである。

 
(let (oldbufcurrent-bufferの値を束縛する)
  (save-excursion         ; 最初のsave-excursion
    バッファを切り替える
      (erase-buffer)
      (save-excursion     ; 2つめのsave-excursion
        oldbufから部分文字列をバッファへ挿入する)))

最初のsave-excursionで、Emacsは、テキストのコピーもとのバッファに 戻ることができる。 これはappend-to-bufferでの用法と同じであり、明らかであろう。 では、2つめは何のためであろう?  insert-buffer-substringは、つねに、 挿入したリージョンの最後にポイントを置く。 2番目のsave-excursionにより、 Emacsは挿入したテキストの先頭にポイントを置くことになる。 多くの場合、ユーザーは、挿入したテキストの先頭にポイントがあることを好む (もちろん、関数copy-to-bufferが完了するともとのバッファに戻る。 しかし、ユーザーがコピー先のバッファに切り替えると、 ポイントはテキストの先頭にある。 つまり、このために2番目のsave-excursionがある)。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5.2 insert-bufferの定義 (2004/08/05)

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=insert-buffer"
"intro2/insert-bufferの定義"へのコメント(無し)

insert-bufferもバッファに関連した関数である。 このコマンドは、別のバッファからカレントバッファへコピーする。 カレントバッファのテキストのリージョンを別のバッファへコピーする append-to-buffercopy-to-bufferとは逆向きである。

また、このコードでは、読み出し専用(read-only)のバッファでの interactiveの使い方と、オブジェクトの名前と名前が参照する オブジェクトとの重要な違いを示す。

insert-buffer のソースコード   
5.2.1 insert-bufferinteractive    When you can read, but not write.
5.2.2 関数insert-bufferの本体 (2004/08/05)    The body has an or and a let.
5.2.3 orのかわりにifを使ったinsert-buffer (2004/08/05)    Using an if instead of an or.
5.2.4 orの本体 (2004/08/05)    How the or expression works.
5.2.5 insert-bufferlet(2004/08/05)    Two save-excursion expressions.



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

insert-buffer のソースコード

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=insert-buffer%20code"
"intro2/insert-bufferのソースコード"へのコメント(無し)

コードはつぎのとおりである。

 
(defun insert-buffer (buffer)
  "Insert after point the contents of BUFFER.
Puts mark after the inserted text.
BUFFER may be a buffer or a buffer name."
  (interactive "*bInsert buffer: ")
  (or (bufferp buffer)
      (setq buffer (get-buffer buffer)))
  (let (start end newmark)
    (save-excursion
      (save-excursion
        (set-buffer buffer)
        (setq start (point-min) end (point-max)))
      (insert-buffer-substring buffer start end)
      (setq newmark (point)))
    (push-mark newmark)))

他の関数と同様に、雛型を使って関数の概略を見ることができる。

 
(defun insert-buffer (buffer)
  "説明文..."
  (interactive "*bInsert buffer: ")
  本体...)



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5.2.1 insert-bufferinteractive

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=insert-buffer%20interactive"
"intro2/insert-bufferinteractive式"へのコメント(無し)

insert-bufferでは、interactiveの引数には、 アスタリスク`*'と`bInsert buffer: 'の2つの部分がある。

読み出し専用バッファ    When a buffer cannot be modified.
interactive式の`b'    An existing buffer or else its name.



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

読み出し専用バッファ

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=Read-only%20buffer"
"intro2/読み出し専用バッファ"へのコメント(無し)

アスタリスクは、読み出し専用バッファ、つまり、 変更できないバッファに対処するためである。 読み出し専用のバッファにinsert-bufferを使うと、 読み出し専用である旨のメッセージがエコー領域に表示され、 端末のベルが鳴るか点滅する。 カレントバッファには挿入できないのである。 アスタリスクのあとにはつぎの部分との区切りに改行は必要ない。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

interactive式の`b'

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=b%20for%20interactive"
"intro2/interactive式の`b'"へのコメント(無し)

interactive式の引数の2番目の部分は、小文字の`b'で始まっている (大文字の`B'で始まるappend-to-bufferのコードと異なる See 節 4.4 append-to-bufferの定義 (2004/08/05))。 小文字の`b'は、insert-bufferの引数は既存のバッファであるか バッファ名であることをLispインタープリタに指示する (大文字の`B'では、バッファが存在しない場合も許す)。 Emacsは、デフォルトのバッファを提示してバッファ名を問い合わせ、 名前の補完も行う。 バッファが存在しないと、メッセージ「No match(一致するものがない)」を表示して 端末のベルを鳴らす。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5.2.2 関数insert-bufferの本体 (2004/08/05)

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=insert-buffer%20body"
"intro2/関数insert-bufferの本体"へのコメント(無し)

関数insert-bufferの本体には、2つの主要な部分、 or式とlet式がある。 or式の目的は、引数bufferがバッファの名前ではなく、 バッファに束縛されていることを保証することである。 let式の本体は、 別のバッファからカレントバッファにコピーするコードである。

関数insert-bufferの2つの式の概略はつぎのとおりである。

 
(defun insert-buffer (buffer)
  "説明文..."
  (interactive "*bInsert buffer: ")
  (or ...
      ...
  (let (変数リスト)
      letの本体... )

or式により、どのようにして引数bufferにバッファ名ではなく バッファが束縛されることが保証されるのかを理解するには、 まず、関数orを理解する必要がある。

そのまえに、関数の最初の部分をifで書き換えて、 すでによく知っている方法で考えてみよう。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5.2.3 orのかわりにifを使ったinsert-buffer (2004/08/05)

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=if%20&%20or"
"intro2/orのかわりにifを使ったinsert-buffer"へのコメント(無し)

必要なことは、bufferの値をバッファ名ではなく バッファそのものとすることである。 値が名前の場合には、対応するバッファそのものを取得する必要がある。

読者の名前が記された名簿を持って、会議場で受け付け係が読者を探している 場面を想像してほしい。 このとき受け付け係には、読者自身ではなく読者の名前が「束縛」されている。 受け付け係が読者を探しあてて読者の腕をとると、 受け付け係には読者が「束縛」される。

Lispでは、このような状況をつぎのように書ける。

 
(if (not (招待客の腕をとっている))
    (招待客を探し、腕をとる))

バッファについても同じことをしたいのである。 バッファそのものを取得していなければ、それを取得したいのである。

(名前ではなく)バッファそのものを持っているかどうかを調べる 述語bufferpを用いれば、つぎのようなコードが書ける。

 
(if (not (bufferp buffer))              ; 判定条件
    (setq buffer (get-buffer buffer)))  ; 真の場合の動作

ここで、if式の判定条件は(not (bufferp buffer))であり、 真の場合の動作は、式(setq buffer (get-buffer buffer))である。

判定条件では、引数がバッファならば関数bufferpは真を返すが、 引数がバッファ名の場合には偽を返す (関数名bufferpの最後の文字は`p'である。 すでに説明したように、`p'をそのように使うのは、 関数が述語(predicate)であることを意味する慣習である。 また、述語とは、ある性質が真であるか偽であるかを調べる関数であった。 See 節 1.8.4 引数に誤った型のオブジェクトを指定 (2004/01/17))。

(bufferp buffer)のまえには関数notがあり、 判定条件はつぎのとおりである。

 
(not (bufferp buffer))

notは、引数が偽の場合には真を、真の場合には偽を返す関数である。 したがって、(bufferp buffer)が真ならばnot式は偽を返し、 偽ならば真を返す。 「not 真」は偽であり、「not 偽」は真である。

この判定条件を使うと、if式はつぎのように動作する。 変数bufferの値がバッファ名ではなく実際のバッファであれば、 判定条件は偽となり、if式の真の場合の動作は評価されない。 変数bufferが実際のバッファであれば何もする必要はないので、 これでよい。

一方、bufferの値がバッファそのものではなくバッファ名の場合には、 判定条件は真を返し、真の場合の動作が評価される。 ここでは、真の場合の動作は(setq buffer (get-buffer buffer))である。 この式では、バッファ名を与えると実際のバッファそのものを 返す関数get-bufferを使う。 setqにより、変数bufferにバッファそのものを設定し、 (バッファ名であった)まえの値を置き換える。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5.2.4 orの本体 (2004/08/05)

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=Insert%20or"
"intro2/orの本体"へのコメント(無し)

関数insert-bufferでのor式の目的は、 引数bufferにバッファ名ではなくバッファが束縛されていることを 保証することである。 上の節では、if式を用いてこれを行う方法を示したが、 関数insert-bufferでは実際にはorを使っている。 これを理解するには、orの動作を理解する必要がある。

関数orは、任意個数の引数を取る。 各引数を順番に評価し、nil以外の値を返した最初の引数の値を返す。 さらに、nil以外の値が初めて返されると、 それよりうしろの引数をいっさい評価しないのも、orの重要な機能である。

or式はつぎのようになる。

 
(or (bufferp buffer)
    (setq buffer (get-buffer buffer)))

orの最初の引数は、式(bufferp buffer)である。 この式は、バッファが名前ではなく実際のバッファであるときには 真(nil以外の値)を返す。 or式では、この場合にはor式は真の値を返し、 そのつぎの式は評価しない。 bufferの値が実際のバッファであれば何もする必要はないので、 これでよい。

一方、bufferの値がバッファ名であると、 (bufferp buffer)の値はnilであり、 Lispインタープリタはor式のつぎの要素を評価する。 つまり、式(setq buffer (get-buffer buffer))を評価する。 この式は、nil以外の値、つまり、変数bufferに設定した値であり、 バッファ名ではなくバッファそのものである。

以上の効果をまとめると、シンボルbufferには、つねに、 バッファ名ではなくバッファそのものが束縛されるのである。 後続行の関数set-bufferはバッファ名ではなくバッファそのものしか 扱えないので、以上の処理が必要なのである。

なお、orを使えば、受け付け係の例はつぎのように書ける。

 
(or (招待客の腕をとっている) (招待客を探し腕をとる))



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5.2.5 insert-bufferlet(2004/08/05)

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=Insert%20let"
"intro2/insert-bufferlet式"へのコメント(無し)

変数bufferがバッファ名ではなくバッファそのものを参照することを 保証したあと、関数insert-bufferlet式に進む。 ここには、3つのローカル変数、startendnewmarkがあり、 それぞれを初期値nilに束縛する。 これらの変数はlet内部の残りの部分で使われ、 letが終わるまでEmacsの同じ名前の変数の出現を一時的に隠す。

letの本体には2つのsave-excursionがある。 まず、内側のsave-excursionを詳しく見てみよう。 その式はつぎのとおりである。

 
(save-excursion
  (set-buffer buffer)
  (setq start (point-min) end (point-max)))

(set-buffer buffer)は、Emacsの注意をカレントバッファから テキストのコピーもとのバッファに向ける。 そのバッファにて、コマンドpoint-minpoint-maxを用いて バッファの先頭と最後を変数startendに設定する。 ここでは、setqにより、1つの式で2つの変数に値を設定する方法も 示している。 setqの最初の引数には第2引数の値が、第3引数には第4引数の値が設定される。

内側のsave-excursionの本体を終了すると、 save-excursionはもとのバッファに戻すが、 startendには、 テキストのコピーもとのバッファの先頭と最後の値が設定されたままである。

外側のsave-excursion式の概要はつぎのとおりである。

 
(save-excursion
  (内側の式save-excursion
     (新しいバッファに切り替えstartendを設定)
  (insert-buffer-substring buffer start end)
  (setq newmark (point)))

関数insert-buffer-substringは、 バッファbufferの始めstartから終わりendまでのリージョンの テキストをカレントバッファにコピーする。 startendのあいだは2番目のバッファ全体であるので、 2番目のバッファ全体が読者が編集しているバッファにコピーされる。 続いて、挿入したテキストの最後にあるポイントを変数newmarkに記録する。

外側のsave-excursionの本体を評価し終えると、 ポイントとマークはもとの位置に戻される。

しかし、新たに挿入したテキストの最後にマークを、先頭にポイントを設定 しておくと便利である。 変数newmarkは挿入したテキストの最後を記録している。 let式の最後の行の式(push-mark newmark)で、 このようにマークを設定する (そのまえのマークの位置も参照できる。 つまりマークリングに記録されているので、C-u C-SPCで戻れる)。 一方、ポイントは挿入したテキストの先頭にあり、 挿入する関数を呼ぶまえの位置のままである。

let式全体はつぎのとおりである。

 
(let (start end newmark)
  (save-excursion
    (save-excursion
      (set-buffer buffer)
      (setq start (point-min) end (point-max)))
    (insert-buffer-substring buffer start end)
    (setq newmark (point)))
  (push-mark newmark))

関数append-to-bufferと同様に、関数insert-bufferは、 letsave-excursionset-bufferを使っている。 さらに、この関数ではorの1つの使い方を示した。 これらのすべての関数は、何度も何度も使う構成部品である。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5.3 beginning-of-bufferの完全な定義 (2004/08/05)

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=beginning-of-buffer"
"intro2/beginning-of-bufferの完全な定義"へのコメント(無し)

関数beginning-of-bufferの基本的な構造はすでに説明した (See 節 4.2 beginning-of-bufferの簡略した定義 (2004/08/04))。

まえに説明したように、引数なしでbeginning-of-bufferを起動すると、 カーソルをバッファの先頭に移動し、マークを移動前の位置に設定する。 ところが、1〜10までの数nを指定して起動すると、 関数はその数をバッファ長を単位としたn/10と解釈し、 Emacsはバッファの先頭からn/10の位置にカーソルを移動する。 したがって、この関数をM-<で呼べばバッファの先頭にカーソルを移動するし、 C-u 7M-<で呼べばバッファの70%のところにカーソルを移動する。 引数に10より大きな数を使うと、バッファの最後にカーソルを移動する。

関数beginning-of-bufferは、引数を指定しても、しなくても呼べる。 つまり、引数は省略できる。

5.3.1 オプションの引数 (2004/08/05)   
5.3.2 引数を指定したbeginning-of-buffer (2004/08/05)    Example with optional argument.
5.3.3 beginning-of-bufferの完全なコード (2004/08/05)   



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5.3.1 オプションの引数 (2004/08/05)

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=Optional%20Arguments"
"intro2/オプションの引数"へのコメント(無し)

特に指定しない限り、Lispは、関数定義で引数を指定した関数は、 引数に渡す値とともに呼ばれることを期待する。 そうでないと、`Wrong number of arguments'のエラーメッセージを得る。

しかし、オプション引数の機能がLispにはある。 引数を省略できることをキーワード(keyword)で Lispインタープリタに指示する。 そのキーワードは&optionalである (`optional'の直前の`&'もキーワードの一部である)。 関数定義において、キーワード&optionalのあとに続く引数には、 関数を呼び出すときにその引数に値が渡されなくてもよい。

したがって、beginning-of-bufferの関数定義の最初の行はつぎのようになる。

 
(defun beginning-of-buffer (&optional arg)

関数全体の概略はつぎのとおりである。

 
(defun beginning-of-buffer (&optional arg)
  "説明文..."
  (interactive "P")
  (push-mark)
    (引数があれば
        移動先の位置を計算する
      さもなければ、
      (point-min))))

この関数は、simplified-beginning-of-bufferに似ているが、 interactive式には引数として"P"があり、 関数goto-charに続けて、引数が指定された場合に 移動先を計算するif式がある点が異なる。

interactive式の"P"は、前置引数がある場合にはそれを 関数に渡すことをEmacsに指示する。 前置引数は、METAキーに続けて数をタイプするか、 C-uをタイプしてから数をタイプする (数をタイプしないと、C-uのデフォルトは4である)。

if式の判定条件は単純で、単に引数argである。 argnil以外の値があるのは、 引数を指定してbeginning-of-bufferが呼ばれた場合であり、 if式の真の場合の動作を評価する。 一方、引数なしでbeginning-of-bufferを呼ぶと、 argの値はnilとなりif式の偽の場合の動作が評価される。 偽の場合の動作は単純であり、point-minである。 このように評価される場合は、goto-char式全体は (goto-char (point-min))となり、 関数beginning-of-bufferの簡略な場合と同じである。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5.3.2 引数を指定したbeginning-of-buffer (2004/08/05)

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=beginning-of-buffer%20opt%20arg"
"intro2/引数を指定したbeginning-of-buffer"へのコメント(無し)

引数を指定してbeginning-of-bufferを呼ぶと、 goto-charに渡す値を計算する式が評価される。 この式は、一見、複雑なように見える。 これには、内側にif式や多くの算術演算がある。 つぎのとおりである。

 
(if (> (buffer-size) 10000)
    ;; バッファサイズが大きな場合の桁溢れを防ぐ!
    (* (prefix-numeric-value arg) (/ (buffer-size) 10))
  (/
   (+ 10
      (*
       (buffer-size) (prefix-numeric-value arg))) 10))

Disentangle beginning-of-buffer   
大きなバッファでの動作   
小さなバッファでの動作   



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

Disentangle beginning-of-buffer

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=Disentangle%20beginning-of-buffer"
"intro2/Disentanglebeginning-of-buffer"へのコメント(無し)

複雑に見える他の式やbeginning-of-buffer内の条件式などと同様に、この式も雛 型に当てはめてみると分かりやすくなる。 ここではif式の雛型を使う。 この式の骨格はつぎのようになる。

 
(if (バッファが大きい
    バッファサイズを10で割ってから、引数を掛ける
  さもなければ、別の計算方法

内側のif式の判定条件では、バッファサイズを調べる。 古いEmacs Lispの第18版では、(大きな数は必要ないので)8百万を超える数は扱えず、 バッファが大きな場合にEmacsがこの制限を超えるような数を 計算する可能性があるので、このような検査をしていた。 注釈中の「桁溢れ(overflow)」は、数が大きすぎることを意味する。Emacs 21 では大 きな数を使えるが,このコードは変更されていない.以前よりもずっと大きなバッファを 見るようになってきたためだ.

バッファが大きい場合とそうでない場合の2つの場合がある。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

大きなバッファでの動作

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=Large%20buffer%20case"
"intro2/大きなバッファでの動作"へのコメント(無し)

beginning-of-bufferでは、内側のif式で バッファサイズが10000文字より大きいかどうかを検査する。 これには関数>と関数buffer-sizeを使う。

つぎのとおりである。

 
(if (> (buffer-size) 10000)

バッファが大きな場合、if式の真の場合の動作が評価される。 (読みやすいように整形すると)その行はつぎのようになる。

 
(*
  (prefix-numeric-value arg)
  (/ (buffer-size) 10))

この式は乗算であり、関数*には2つの引数を渡す。

第1引数は(prefix-numeric-value arg)である。 interactiveの引数に"P"を使うと、 関数に引数として渡される値は数ではなく、「生の前置引数(raw prefix argument)」 である(数のリスト)。 数値演算を施すには、変換する必要があり、 それにはprefix-numeric-valueを使えばよい。

第2引数は(/ (buffer-size) 10)である。 この式は、バッファの大きさの数を10で割る。 これにより、バッファサイズの1/10に相当する文字数が得られる (Lispでは、*を乗算に使うように、/を除算に使う)。

乗算の式全体では、この値に引数の値を掛ける。 乗算はつぎのようになる。

 
(* 前置引数の値
   バッファの文字数の1/10)

たとえば、前置引数が`7'ならば、1/10に相当する値に7を掛け、 バッファの70%の位置になる。

つまり、バッファが大きな場合には、以上の結果から、 goto-char式はつぎのようになる。

 
(goto-char (* (prefix-numeric-value arg)
              (/ (buffer-size) 10)))

これにより、望みの位置にカーソルが移動する。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

小さなバッファでの動作

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=Small%20buffer%20case"
"intro2/小さなバッファでの動作"へのコメント(無し)

バッファが10000文字未満の場合には、少々異なる計算を行う。 最初の計算方法で十分であり、このようなことは不要と考える読者もいよう。 しかし、小さなバッファでは、最初の計算方法では、目的の行に正しく カーソルを移動できず、2番目の計算方法のほうがよい。

コードはつぎのとおりである。

 
(/ (+ 10 (* (buffer-size) (prefix-numeric-value arg))) 10))

このコードでは、関数がどのように括弧の中に入れ子になっているかがわかれば、 何が起こるかを理解できる。 入れ子になった式をより深く字下げして整形すると読みやすくなる。

 
  (/
   (+ 10
      (*
       (buffer-size)
       (prefix-numeric-value arg)))
   10))

括弧をよく見ると、もっとも内側の演算は(prefix-numeric-value arg)であり、 生の引数を数に変換する。 つぎの式で、この数にバッファサイズを掛ける。

 
(* (buffer-size) (prefix-numeric-value arg)

この乗算により、バッファのサイズよりも大きな数を得る。 たとえば、引数が7ならば、7倍大きい。 この数に10を加えてから10で割り、バッファの目的の位置より1だけ大きな数を得る。

この計算結果をgoto-charに渡してカーソルを移動する。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5.3.3 beginning-of-bufferの完全なコード (2004/08/05)

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=beginning-of-buffer%20complete"
"intro2/beginning-of-bufferの完全なコード"へのコメント(無し)

関数beginning-of-bufferの完全なテキストをつぎに示す。

 
(defun beginning-of-buffer (&optional arg)
  "Move point to the beginning of the buffer;
leave mark at previous position.
With arg N, put point N/10 of the way
from the true beginning.
Don't use this in Lisp programs!
\(goto-char (point-min)) is faster
and does not set the mark."
  (interactive "P")
  (push-mark)
  (goto-char
   (if arg
       (if (> (buffer-size) 10000)
           ;; Avoid overflow for large buffer sizes!
           (* (prefix-numeric-value arg)
              (/ (buffer-size) 10))
         (/ (+ 10 (* (buffer-size)
                     (prefix-numeric-value arg)))
            10))
     (point-min)))
  (if arg (forward-line 1)))

2つの小さな点を除いて、この関数はまえに述べたような動作をする。 最初の点は、説明文字列に関する部分であり、 第2の点は関数の最後の行である。

説明文字列では、つぎの式を参照している。

 
\(goto-char (point-min))

この式の最初の括弧のまえには`\'がある。 この`\'は、シンボリック式として評価するのではなく 説明文として式を表示するようにLispインタープリタに指示する。

コマンドbeginning-of-bufferの最後の行は、 引数を指定してコマンドを起動した場合に、ポイントを次行の先頭に移動する。

 
(if arg (forward-line 1)))

これにより、バッファの適切なn/10の位置の直後の行の先頭にカーソルを置く。 これは、すくなくともバッファの指定されたn/10の位置にカーソルを位置決めして 見栄えをよくするためのもので、必要ないことかもしれないが、 こうしないと不満のもとになる。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5.4 復 習 (2004/08/05)

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=Second%20Buffer%20Related%20Review"
"intro2/復 習"へのコメント(無し)

本章で説明したことがらをまとめておく。

or
各引数を順番に評価し、nil以外の値を返した最初の値を返す。 nil以外の値を返すものがなければ、nilを返す。 つまり、引数の最初の真の値を返す。 1つでも真のものがあれば、真の値を返す。

and
各引数を順番に評価し、すべてがnilならばnilを返す。 nilでないものがなければ、最後の引数の値を返す。 つまり、すべての引数が真ならば真の値を返す。

&optional
関数定義に引数を省略できることを表すキーワード。 つまり、必要ならば、引数を与えなくても関数を評価できる。

prefix-numeric-value
(interactive "P")で得た「生の前置引数」を数値に変換する。

forward-line
ポイントをつぎの行の先頭に移動する。 1より大きな引数を与えた場合には、その行数だけポイントを進める。 ポイントを指定量だけ進められない場合には、 forward-lineは可能なだけ進めて、指定量に満たない行数を返す。

erase-buffer
カレントバッファの全内容を削除する。

bufferp
引数がバッファならtを、さもなければnilを返す。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

5.5 optional引数の演習問題

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=intro2&node=optional%20Exercise"
"intro2/optional引数の演習問題"へのコメント(無し)

省略できる引数に指定した数がfill-columnの値に比べて 大きいか小さいかをメッセージに表示する対話的関数を書いてみよ。 ただし、関数に引数を渡されなかった場合のデフォルトは56とする。


[ << ] [ >> ]           [表紙] [目次] [索引] [検索] [上端 / 下端] [?]