[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
正規表現の探索は、GNU Emacsでは非常に多用されている。 2つの関数forward-sentence
とforward-paragraph
は、 これらの探索のよい例である。
正規表現の探索に ついては、節 `Regular Expression Search' in
forward-sentence
のコードは、文末を表す可能性のある 文字のパターンを探索し、そこへポイントを移動する。
関数forward-sentence
のコードを説明するまえに、 文末を表すパターンとはどんなものであるかを考えることも価値がある。 このパターンについては次節で説明する。 続いて、正規表現の探索関数re-search-forward
を説明する。 さらに、関数forward-sentence
を説明する。 そして、本章の最後の節では、関数forward-paragraph
を説明する。 forward-paragraph
は複雑な関数であり、新たな機能もいくつか紹介する。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
sentence-end
のための正規表現sentence-end
のための正規表現"へのコメント(無し)
シンボルsentence-end
は、文末を表すパターンに束縛される。 この正規表現はどんなものであるべきか?
明らかに、文は、ピリオドや疑問符や感嘆符で終わる。 もちろん、これら3つの文字のうちの1つで終わる節のみを文末と考えるべきである。 つまり、パターンにはつぎの文字集合が含まれるべきである。
[.?!] |
しかし、ピリオドや疑問符や感嘆符は文の途中に使われることもあるので、 forward-sentence
が単純にこれら3つの文字に移動してほしくはない。 たとえば、ピリオドは省略形のあとにも使われる。 つまり、別の情報が必要なのである。
慣習としては、文のうしろには2つの空白を置くが、 文の途中のピリオドや疑問符や感嘆符のうしろには空白を1つだけ置く。 つまり、ピリオドや疑問符や感嘆符のあとに空白が2つあれば、 文末を表すと考えられる。 しかし、ファイルでは、2つの空白のかわりに、タブだったり行末だったりもする。 つまり、正規表現は、これらの3つの場合を含む必要がある。 これらは、つぎのように表せる。
\\($\\| \\| \\) ^ ^^ TAB SPC |
ここで、`$'は行末を表し、また、式の中にタブがあるのか空白が2つあるのか わかるようにしてある。 どちらも式の中には実際の文字を入れる。
括弧と縦棒のまえには2つのバックスラッシュ`\\'が必要である。 Emacsでは、最初のバックスラッシュはそれに続くバックスラッシュをクオートし、 2番目のバックスラッシュは、それに続く括弧や縦棒が特別な文字であることを表す。
また、つぎのように、文には複数個の復帰が続いてもよい。
[ ]* |
タブや空白のように、正規表現に復帰を入れるにはそのまま入れる。 アスタリスクは、RETが0回以上繰り返されることを表す。
文末は、ピリオドや疑問符や感嘆符のあとに適切な空白が続くだけはない。 空白のまえが、閉じ引用符や閉じ括弧の類でもよい。 もちろん、空白のまえにこれらが複数個あってもよい。 これらはつぎのような正規表現を必要とする。
[]\"')}]* |
この式で、最初の`]'は正規表現の最初の文字である。 2番目の文字は`"'であり、そのまえには`\'があるが Emacsに`"'が特別な文字ではないことを指示する。 残りの3つの文字は`''と`)'と`}'である。
これらすべてで、文末に一致する正規表現のパターンが何であるかを表す。 そして、もちろん、sentence-end
を評価すると つぎのような値になっていることがわかる。
sentence-end => "[.?!][]\"')}]*\\($\\| \\| \\)[ ]*" |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
re-search-forward
re-search-forward
"へのコメント(無し)
関数re-search-forward
は、関数search-forward
にとてもよく似ている (See 節 8.1.3 関数search-forward
)。
re-search-forward
は正規表現を探索する。 探索に成功すると、みつかった文字列の最後の文字の直後にポイントを置く。 逆向きの探索の場合には、みつかった文字列の最初の文字の直前にポイントを置く。 真値としてt
を返すようにre-search-forward
に指示できる (したがって、ポイントの移動は「副作用」である)。
search-forward
のように、関数re-search-forward
は4つの引数を取る。
nil
を指定すると、探索に失敗した場合、 関数はエラーを通知(し、メッセージを表示)する。 それ以外の値を指定すると、探索に失敗した場合はnil
を返し、 探索に成功するとt
を返す。
re-search-forward
に逆向き探索を指示する。re-search-forward
の雛型はつぎのとおりである。
(re-search-forward "正規表現" 探索範囲 探索失敗時の動作 繰り返し回数) |
第2引数、第3引数、第4引数は省略できる。 しかし、最後の2つの引数のいずれか、あるいは、両者に値を渡したい場合には、 そのまえにある引数すべてに値を渡す必要がある。 さもないと、Lispインタープリタはどの引数に値を渡すのかを誤解することになる。
関数forward-sentence
では、 正規表現は変数sentence-end
の値であり、つぎのとおりである。
"[.?!][]\"')}]*\\($\\| \\| \\)[ ]*" |
探索の範囲は、(文は段落を越えることはないので)段落の末尾までである。 探索に失敗すると、関数はnil
を返す。 繰り返し回数は、関数forward-sentence
の引数で与える。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
forward-sentence
forward-sentence
"へのコメント(無し)
カーソルを文単位で先へ進めるコマンドは、 Emacs Lispにおいて正規表現の探索の使い方を示す直接的な例である。 もちろん、関数は単なる例よりは長くて複雑である。 これは、関数が前向きと同時に逆向き探索にも対応しており、 文単位で複数回進めることもできるからである。 この関数は、通常、M-eのキーコマンドにバインドされている。
forward-sentence
のコードをつぎに示す。
(defun forward-sentence (&optional arg) "Move forward to next sentence-end. With argument, repeat. With negative argument, move backward repeatedly to sentence-beginning. Sentence ends are identified by the value of sentence-end treated as a regular expression. Also, every paragraph boundary terminates sentences as well." (interactive "p") (or arg (setq arg 1)) (while (< arg 0) (let ((par-beg (save-excursion (start-of-paragraph-text) (point)))) (if (re-search-backward (concat sentence-end "[^ \t\n]") par-beg t) (goto-char (1- (match-end 0))) (goto-char par-beg))) (setq arg (1+ arg))) (while (> arg 0) (let ((par-end (save-excursion (end-of-paragraph-text) (point)))) (if (re-search-forward sentence-end par-end t) (skip-chars-backward " \t\n") (goto-char par-end))) (setq arg (1- arg)))) |
一見すると関数は長いが、骨格を見てからその筋肉を見るのが最良であろう。 骨格を見るには、もっとも左のコラムから始まる式を見ればよい。
(defun forward-sentence (&optional arg) "説明文..." (interactive "p") (or arg (setq arg 1)) (while (< arg 0) whileループの本体 (while (> arg 0) whileループの本体 |
だいぶ簡単に見える。 関数定義は、説明文、interactive
式、or
式、 while
ループから成る。
これらの各部分を順番に見てみよう。
説明文は、十分でわかりやすい。
関数には、interactive "p"
の宣言がある。 これは、もしあれば処理した前置引数を引数として関数に渡すことを意味する (これは数である)。 関数に(省略できる)引数が渡されないと、引数arg
には1が束縛される。 forward-sentence
が非対話的に引数なしで呼ばれた場合には、 arg
にはnil
が束縛される。
or
式で前置引数を処理する。 これは、arg
に値が束縛されていればそのままにするが、 arg
にnil
が束縛されているときにはarg
の値を1にする。
while
ループTwo while
loops.正規表現の探索 A regular expression search.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
while
ループwhile
ループ"へのコメント(無し)
or
式に続けて2つのwhile
ループがある。 最初のwhile
には、forward-sentence
の前置引数が負の数のときに 真となる判定条件がある。 これは、逆向き探索用である。 このループの本体は2番目のwhile
節の本体に似ているが、同一ではない。 このwhile
ループを飛ばして、2番目のwhile
に注目しよう。
2番目のwhile
ループは、ポイントを先へ進めるためのものである。 その骨格はつぎのように読める。
(while (> arg 0) ; 判定条件
(let 変数リスト
(if (判定条件)
真の場合の動作
偽の場合の動作
(setq arg (1- arg)))) ;
|
while
ループは減少方式である (See 節 11.1.4 減少カウンタによるループ)。 これは、カウンタ(変数arg
)が0より大きい限り真を返す判定条件と、 ループを1回廻るごとにカウンタの値を1減らす減少式を持つ。
コマンドのもっとも一般的な用法であるが、 forward-sentence
に前置引数を与えないとarg
の値は1なので、 このwhile
ループは1回だけ廻る。
while
ループの本体は、let
式から成り、 ローカル変数を作って束縛し、本体はif
式である。
while
ループの本体はつぎのとおりである。
(let ((par-end (save-excursion (end-of-paragraph-text) (point)))) (if (re-search-forward sentence-end par-end t) (skip-chars-backward " \t\n") (goto-char par-end))) |
let
式は、ローカル変数par-end
を作って束縛する。 あとでわかるように、このローカル変数は、正規表現の探索の範囲を 制限するためのものである。 段落の中で正しい文末を探せなかった場合には、段落の末尾で止まる。
まず、どのようにしてpar-end
に段落の末尾が束縛されるかを説明する。 let
は、par-end
の値に、 Lispインタープリタがつぎの式を評価した値を設定する。
(save-excursion (end-of-paragraph-text) (point)) |
この式では、(end-of-paragraph-text)
でポイントを段落の末尾に移動し、 (point)
でポイントの値を返す。 そして、save-excursion
がポイントをもとの位置に戻す。 したがって、let
は、save-excursion
が返した値をpar-end
に 束縛するが、これは段落の末尾の位置である (関数(end-of-paragraph-text)
は、これから説明する forward-paragraph
を使う)。
Emacsは、続いて、let
の本体を評価する。 それはつぎのようなif
式である。
(if (re-search-forward sentence-end par-end t) ; 判定条件 (skip-chars-backward " \t\n") ; 真の場合の動作 (goto-char par-end))) ; 偽の場合の動作 |
if
は、第1引数が真かどうかを調べ、 そうならば、真の場合の動作を評価し、さもなければ、 Emacs Lispインタープリタは偽の場合の動作を評価する。 if
式の判定条件は、正規表現の探索である。
関数forward-sentence
のこのような実際の動作は奇妙に思えるかもしれないが、 これはLispでこの種の操作を行う一般的な方法である。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
関数re-search-forward
は文末を探す、 つまり、正規表現sentence-end
で定義されたパターンを探す。 パターンがみつかれば、つまり、文末がみつかれば、 関数re-search-forward
はつぎの2つのことを行う。
re-search-forward
は、副作用を及ぼす。 つまり、みつけた文字列の直後にポイントを移動する。
re-search-forward
は真値を返す。 この値をif
が受け取り、探索が成功したことを知る。ポイントを移動するという副作用は、関数if
に探索成功の値が 返されるまえに完了する。
関数if
が、re-search-forward
の呼び出しに成功し真値を受け取ると、 if
は真の場合の動作、式(skip-chars-backward " \t\n")
を評価する。 この式は、目に見える文字がみつかるまで逆向きに空白やタブや復帰を飛ばして 目に見える文字の直後にポイントを移動する。 ポイントは文末のパターンの直後にあったので、 この操作により、ポイントは文末の目に見える文字、 普通はピリオドの直後にポイントを置く。
一方、関数re-search-forward
が文末のパターンの検索に失敗すると、 関数は偽を返す。 するとif
は第3引数、つまり、(goto-char par-end)
を評価し、 段落の末尾にポイントを移動する。
正規表現の探索はとても便利であり、if
式の判定条件でも使っている re-search-forward
はパターンの例題として簡便である。 読者もこのパターンをしばしば利用するであろう。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
forward-paragraph
:関数の宝庫forward-paragraph
:関数の宝庫"へのコメント(無し)
関数forward-paragraph
は、段落の末尾にポイントを進める。 普通はM-}にバインドされており、 let*
やmatch-beginning
やlooking-at
の それ自体で重要な関数を利用している。
詰め込み接頭辞(fill prefix)で始まる行から成る段落を処理するため、 forward-paragraph
の関数定義は、forward-sentence
の関数定義に 比べてとても長い。
詰め込み接頭辞は、各行の先頭で繰り返すことができる文字の文字列から成る。 たとえば、Lispコードでは、段落ほどもある注釈の各行は`;;; 'で 始める約束になっている。 テキストモードでは4つの空白を詰め込み接頭辞とするのが一般的であり、 字下げした段落になる (詰め込み接頭辞について詳しくは、 See 節 `Fill Prefix' in
詰め込み接頭辞があると、関数forward-paragraph
は、 もっとも左のコラムから始まる行から成る段落の末尾を探すだけでなく、 詰め込み接頭辞で始まる行を含む段落の末尾も探す必要がある。
さらに、段落を区切る空行の場合には、 詰め込み接頭辞を無視するほうが実用的である。 これも複雑さが増す理由である。
関数forward-paragraph
の全体を示すかわりに、その一部だけを示す。 前準備もなしに読むと、気力をくじかれる!
関数の概略はつぎのとおりである。
(defun forward-paragraph (&optional arg) "説明文..." (interactive "p") (or arg (setq arg 1)) (let* 変数リスト (while (< arg 0) ; 戻すコード ... (setq arg (1+ arg))) (while (> arg 0) ; 進めるコード ... (setq arg (1- arg))))) |
関数の始めの部分は決まりきっていて、 省略できる1個の引数から成る関数の引数リストである。 これに説明文が続く。
宣言interactive
の小文字の`p'は、もしあれば処理した前置引数を 関数に渡すことを意味する。 これは数であり、何個の段落だけ先へ進むかを表す繰り返し回数である。 つぎのor
式は、関数に引数が渡されなかった場合の共通処理で、 対話的にではなく他のコードから関数が呼び出された場合にこうなる (See 節 12.3 forward-sentence
)。 この関数の馴染みのある部分はこれで終わりである。
let*
式The let*
expression.先へ進める while
ループThe forward motion while
loop.段落のあいだ Movement between paragraphs. 段落の中 Movement within paragraphs. 詰め込み接頭辞なし When there is no fill prefix. 詰め込み接頭辞あり When there is a fill prefix. まとめ Summary of forward-paragraph
code.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
let*
式let*
式"へのコメント(無し)
関数forward-paragraph
のつぎの行はlet*
式で始まる。 これはこれまでに見てきた式とは種類が異なる。 シンボルはlet*
であり、let
ではない。
スペシャルフォームlet*
はlet
に似ているが、 Emacsは1つずつ順に各変数を設定し、変数リストのうしろの変数では、 Emacsがすでに設定した変数リストのまえの部分の変数の値を利用してもよいところが 異なる。
この関数のlet*
式では、Emacsは2つの変数、 fill-prefix-regexp
とparagraph-separate
を束縛する。 paragraph-separate
に束縛される値は、 fill-prefix-regexp
に束縛された値に依存する。
それぞれを順番に見てみよう。 シンボルfill-prefix-regexp
には、つぎのリストを評価した値が設定される。
(and fill-prefix (not (equal fill-prefix "")) (not paragraph-ignore-fill-prefix) (regexp-quote fill-prefix)) |
この式の先頭要素は関数and
である。
関数and
は、引数の1つが値nil
を返すまで各引数を評価する。 nil
を返した場合には、and
式もnil
を返す。 しかし、値nil
を返す引数がなければ、最後の引数を評価した値を返す (そのような値はnil
以外なので、Lispでは真と解釈される)。 いいかえれば、and
式は、すべての引数が真であるときに限り真値を返す
ここでは、つぎの4つの式を評価して真値(つまり、非nil
)が得られれば、 変数fill-prefix-regexp
には非nil
の値が束縛される。 さもなければ、fill-prefix-regexp
はnil
に束縛される。
fill-prefix
nil
を返す。
(not (equal fill-prefix "")
(not paragraph-ignore-fill-prefix)
paragraph-ignore-fill-prefix
に t
などの真値を設定してオンになっているとnil
を返す。
(regexp-quote fill-prefix)
and
の最後の引数である。 and
のすべての引数が真ならば、 この式を評価した結果の値をand
式が返し、 変数fill-prefix-regexp
に束縛される。 このand
式が正しく評価されると、fill-prefix-regexp
には、 関数regexp-quote
で修正したfill-prefix
の値が束縛される。 regexp-quote
は、文字列を読み取り、その文字列のみに一致し それ以外には一致しない正規表現を返す。 つまり、fill-prefix-regexp
には、 詰め込み接頭辞があれば、詰め込み接頭辞だけに一致するものが設定される。 さもなければ、この変数にはnil
が設定される。
let*
式の2番目のローカル変数はparagraph-separate
である。 これにはつぎの式を評価した値が束縛される。
(if fill-prefix-regexp (concat paragraph-separate "\\|^" fill-prefix-regexp "[ \t]*$") paragraph-separate))) |
この式は、let
ではなくlet*
を使った理由を示している。 if
の判定条件は、変数fill-prefix-regexp
がnil
であるか それ以外の値であるかに依存している。
fill-prefix-regexp
に値がなければ、Emacsはif
式の偽の場合の動作を 評価して、ローカル変数にparagraph-separate
を束縛する (paragraph-separate
は、段落の区切りに一致する正規表現である)。
一方、fill-prefix-regexp
に値があれば、 Emacsはif
式の真の場合の動作を評価して、 paragraph-separate
には、 パターンとしてfill-prefix-regexp
を含む正規表現を束縛する。
特に、paragraph-separate
には、段落を区切るもとの正規表現に、 fill-prefix-regexp
に空行が続くという代替パターンを連結したものを 設定する。 `^'はfill-prefix-regexp
が行の先頭から始まることを指定し、 "[ \t]*$"
は行末に空白が続いてもよいことを指定する。 `\\|'は、この部分がparagraph-separate
に対する 代替の正規表現であることを指定する。
ではlet*
の本体に移ろう。 let*
の本体の始めの部分は、関数に負の引数を与えた場合の処理であり、 逆向きに戻す。 本節では、割愛する。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
while
ループwhile
ループ"へのコメント(無し)
let*
の本体の2番目の部分は、先へ進める処理を行う。 これは、arg
の値が0より大きい限り繰り返すwhile
ループである。 関数のもっとも一般的な使い方では引数の値は1であり、 while
ループの本体がちょうど1回だけ評価され、 カーソルを1段落分進める。
この部分では3つの状況を処理する。 ポイントが段落のあいだにある場合、 ポイントが段落の中にあり詰め込み接頭辞がある場合、 ポイントが段落の中にあり詰め込み接頭辞がない場合である。
while
ループはつぎのとおりである。
(while (> arg 0) (beginning-of-line) ;; 段落のあいだ (while (prog1 (and (not (eobp)) (looking-at paragraph-separate)) (forward-line 1))) ;; 段落の中で、詰め込み接頭辞あり (if fill-prefix-regexp ;; 詰め込み接頭辞がある; 段落の始まりのかわりに使う (while (and (not (eobp)) (not (looking-at paragraph-separate)) (looking-at fill-prefix-regexp)) (forward-line 1)) ;; 段落の中で、詰め込み接頭辞なし (if (re-search-forward paragraph-start nil t) (goto-char (match-beginning 0)) (goto-char (point-max)))) (setq arg (1- arg))) |
減少式として式(setq (1- arg))
を使っているので、 減少カウンタのwhile
ループであることはすぐにわかる。 ループの本体は3つの式から成る。
;; 段落のあいだ (beginning-of-line) (while whileの本体) ;; 段落の中で、詰め込み接頭辞あり (if 判定条件 真の場合の動作 ;; 段落の中で、詰め込み接頭辞なし 偽の場合の動作 |
Emacs Lispインタープリタがwhile
ループの本体を評価するとき、 最初に行うことは、式(beginning-of-line)
を評価して ポイントを行の先頭に移動することである。 続いて、内側のwhile
ループがくる。 このwhile
ループは、段落のあいだに空行があれば、 そこからカーソルを移動するためのものである。 最後のif
式で、ポイントを段落の末尾に実際に移動する。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
まず、内側のwhile
ループを説明しよう。 このループは、ポイントが段落のあいだにある場合を扱う。 3つの新たな関数、prog1
、eobp
、looking-at
を使っている。
prog1
は関数progn
に似ているが、 引数を順番に評価し終えたあとに、第1引数の値を式全体の値として返す点が異なる (progn
は、式の値としては最後の引数の値を返す)。 prog1
の2番目以降の引数は、副作用のためだけに評価される。
eobp
は`End Of Buffer(バッファの終わり)P'の略であり、 ポイントがバッファの最後にあるときに真を返す関数である。
looking-at
は、ポイントに続くテキストがlooking-at
の引数に 渡した正規表現に一致する場合に真を返す関数である。説明しているwhile
ループはつぎのとおりである。
(while (prog1 (and (not (eobp)) (looking-at paragraph-separate)) (forward-line 1))) |
このwhile
ループには本体がない! ループの判定条件はつぎの式である。
(prog1 (and (not (eobp)) (looking-at paragraph-separate)) (forward-line 1))) |
prog1
の第1引数はand
式である。 この中では、ポイントがバッファの最後にあるかどうか、 段落を区切る正規表現に一致するものがポイントに続いているかどうか、 を検査する。
カーソルがバッファの最後になくて、 カーソルに続く文字の列が段落を区切るものであれば、and
式は真になる。 and
式を評価したあと、Lispインタープリタはprog1
の第2引数、 forward-line
を評価する。 これは、ポイントを1行分先へ進める。 しかし、prog1
が返す値は第1引数の値であるため、 ポイントがバッファの最後になくて、かつ、 段落のあいだにあるかぎり、while
ループは繰り返される。 最終的にポイントが段落に達するとand
式は偽になる。 しかし、いずれにしてもコマンドforward-line
は実行されることに 注意してほしい。 つまり、段落と段落のあいだでポイントを移動したときには、 段落の第2行目の先頭にポイントが移動するのである。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
外側のwhile
ループのつぎの式はif
式である。 変数fill-prefix-regexp
がnil
以外の値を持っている場合には、 Lispインタープリタはif
の真の場合の動作を評価し、 fill-prefix-regexp
の値がnil
の場合、 つまり、詰め込み接頭辞がない場合には偽の場合の動作を評価する。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
詰め込み接頭辞がない場合のコードを見るほうが簡単である。 このコードは、さらに内側にif
式を含み、つぎのようになっている。
(if (re-search-forward paragraph-start nil t) (goto-char (match-beginning 0)) (goto-char (point-max))) |
この式は、ほとんどの人がコマンドforward-paragraph
の主要な目的で あると考えることを行う。 つぎの段落の先頭をみつけるための正規表現の探索を行い、 みつかればポイントをそこへ移動する。 段落の始まりがみつからなければ、バッファの参照可能なリージョンの最後に ポイントを移動する。
この部分で馴染みがないのはmatch-beginning
の使い方であろう。 これもわれわれにとっては新しいものである。 関数match-beginning
は、直前の正規表現の探索で一致した テキストの先頭位置を与える数を返す。
関数match-beginning
を使うのは、探索の性質のためである。 普通の探索であれ正規表現の探索であれ、 探索に成功するとポイントは探し出したテキストの終わりに移動する。 この場合では、探索に成功すると、 ポイントはparagraph-start
に一致したテキストの終わりに移動するが、 これは、今の段落の末尾ではなく、つぎの段落の始まりである。
しかし、つぎの段落の始まりにではなく、今の段落の末尾にポイントを 置きたいのである。 段落のあいだには何行かの空行がありえるので、2つの位置は異なるであろう。
引数に0を指定すると、match-beginning
は、直前の正規表現の探索で 一致したテキストの始まりの位置を返す。 この例では、直前の正規表現の探索は、paragraph-start
を探したものであり、 match-beginning
は、パターンの終わりではなく始まりの位置を返す。 始まりの位置は、段落の末尾である。
(引数に正の数を指定すると、関数match-beginning
は、 直前の正規表現の中の括弧表現にポイントを置く。 これは便利な機能である。)
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
説明したばかりの内側のif
式は、詰め込み接頭辞の有無を調べる if
式の偽の場合の動作である。 詰め込み接頭辞がある場合には、このif
式の真の場合の動作が評価される。 それはつぎのとおりである。
(while (and (not (eobp)) (not (looking-at paragraph-separate)) (looking-at fill-prefix-regexp)) (forward-line 1)) |
この式は、つぎの3つの条件が真である限り、ポイントを1行進める。
このまえにある関数forward-paragraph
で行の先頭にポイントが移動している ことを思い出さないと、最後の条件に惑わされるかもしれない。 つまり、テキストに詰め込み接頭辞がある場合、 関数looking-at
はそれをみつけるのである。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
まとめると、関数forward-paragraph
がポイントを進めるときには、 つぎのことを行う。
復習のために、ここで説明したコードを わかりやすいように整形して以下に記す。
(interactive "p") (or arg (setq arg 1)) (let* ( (fill-prefix-regexp (and fill-prefix (not (equal fill-prefix "")) (not paragraph-ignore-fill-prefix) (regexp-quote fill-prefix))) (paragraph-separate (if fill-prefix-regexp (concat paragraph-separate "\\|^" fill-prefix-regexp "[ \t]*$") paragraph-separate))) ポインタをまえへ戻すコード(省略) ... (while (> arg 0) ; 進めるコード (beginning-of-line) (while (prog1 (and (not (eobp)) (looking-at paragraph-separate)) (forward-line 1))) (if fill-prefix-regexp (while (and (not (eobp)) ; 真の場合の動作 (not (looking-at paragraph-separate)) (looking-at fill-prefix-regexp)) (forward-line 1)) ; 内側のifの偽の場合の動作 (if (re-search-forward paragraph-start nil t) (goto-char (match-beginning 0)) (goto-char (point-max)))) (setq arg (1- arg))))) ; 減少式 |
関数のforward-paragraph
の完全な定義には、 以上の進めるコードに加えて戻るコードも含まれる。
GNU Emacsで読んでいて、関数全体を見たい場合には、 M-.(find-tag
)とタイプし、問い合わせに対して関数名を与える。 関数find-tag
がタグテーブルの名前を問い合わせてきたら、 読者のサイトのディレクトリ`emacs/src'のタグファイルの名前を与える。 ディレクトリ`emacs/src'は、 `/usr/local/lib/emacs/19.23/src/TAGS'のようなパス名であろう (ディレクトリ`emacs/src'の正確なパスは、 Emacsをどのようにインストールしたかに依存する。 わからない場合には、C-h iとタイプしてInfoに入り、 C-x C-fとタイプしてディレクトリ`emacs/info'のパスを調べる。 タグファイルは`emacs/src'のパスに対応する場合が多いが、 infoファイルをまったく別の場所に置く場合もある)。
ディレクトリにタグファイルがなくても、 読者専用の`TAGS'ファイルを作成できる。 See 節 Create Your Own `TAGS' File.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
ソースを調べ廻る際の補助として、個人用のタグファイルを作成できる。 筆者のディレクトリ`~/emacs'には、137個の`.el'ファイルがあり、 そのうちの17個をロードしている。 読者のディレクトリ`~/emacs'にも多数のファイルがある場合、 タグファイルを作っておけば望みの関数にジャンプでき、 grep
などのツールで関数名を探すより簡単である。
タグファイルを作成するには、Emacsのディストリビューションに含まれる プログラムetags
を使う。 普通、Emacsを作るときにetags
もコンパイルされてインストールされる (etags
はEmacs Lispの関数でもEmacsの一部でもない。 Cのプログラムである)。
タグファイルを作るには、タグファイルを作りたいディレクトリにまず移動する。 Emacsの中では、コマンドM-x cdで行うか、 そのディレクトリのファイルを訪問するか、 C-x d(dired
)でディレクトリを表示する。 続いて、
M-! etags *.el |
とタイプすれば、タグファイル`TAGS'が作られる。 プログラムetags
では、普通のシェルの「ワイルドカード」を使える。 たとえば、2つのディレクトリから1つのタグファイル`TAGS'を作るには、 2番目のディレクトリを`../elisp/'とすると、 つぎのようなコマンドを入力する。
M-! etags *.el ../elisp/*.el |
また
M-! etags --help |
とタイプすれば、etags
が受け付けるオプションの一覧が表示される。
プログラムetags
は、Emacs Lisp、Common Lisp、Scheme、C、 Fortran、Pascal、LaTeX、ほとんどのアセンブラを扱える。 このプログラムには言語を指定するスイッチはない。 ファイル名とその内容から入力ファイルの言語を認識する。
また、自分でコードを書いているときにすでに書いてある関数を参照するときにも、 `etags'はとても助けになる。 新たに関数を書き加えるごとにetags
を走らせれば、 それらはタグファイル`TAGS'の一部になる。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
説明した関数のうち、いくつかを簡素にまとめておく。
while
nil
を返す。 (式は、その副作用のためだけに評価される。)
たとえば、
(let ((foo 2)) (while (> foo 0) (insert (format "foo is %d.\n" foo)) (setq foo (1- foo)))) => foo is 2. foo is 1. nil |
insert
は、ポイント位置に引数を挿入する。 関数format
は、message
が引数を書式付けするように、 引数を書式付けした文字列を返す。 \n
は改行になる。)
re-search-forward
search-forward
のように4つの引数を取る。
nil
を返すかエラーメッセージを返すかを指定する。 省略できる。
let*
たとえば、
(let* ((foo 7) (bar (* 3 foo))) (message "`bar' is %d." bar)) => `bar' is 21. |
match-beginning
looking-at
t
を返す。
eobp
t
を返す。 バッファの参照可能な部分の最後は、 ナロイングしていなければバッファの最後であり、 ナロイングしていればその部分の最後である。
prog1
たとえば、
(prog1 1 2 3 4) => 1 |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
re-search-forward
の演習問題re-search-forward
の演習問題"へのコメント(無し)
the-the
。
[ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |