優しい Emacs-Lisp 講座

メジャー・モードを作ろう

Emacs-Lisp without a Hustle

pcs39334@asciinet.or.jp
yuuji@ae.keio.ac.jp
(C) 1991-1995 by HIROSE, Yuuji


メジャーモードを書こう

多くの Emacs(-Lisp) 関係の教本の常識を無視して、いきなりここから 始めます。これがもっとも早い道だと信じるからで、事実私もそうしてきました。

メジャーモードってなに

多くの Emacs-Lisp プログラマがで最終的に目指す目標の一つが、「メ ジャーモードが書けるようになる」ことでしょう。メジャーモードとは、例えば c-mode のように対象となるテキストの種別に適した、もっと言うと「専用 の」編集モードの事を指します。

GNU Emacs では、編集ファイルのファイル名のパターンと、そのファイ ルを編集する時に用いるメジャーモードを決定するものとして、変数 auto-mode-alist を使用しています。これは今まで、いろいろなパッケー ジをインストールしたことのある人なら、設定したことがあるでしょうから、詳し い構造などについては述べません。

メジャーモードの必須条件

では、早速メジャーモードを書いてみましょう。その前に、メジャーモードの備 えているべき最低限の条件について整理してみましょう。

たったこれだけなのです。

モード名の設定

モード名は、変数 major-mode にシンボルとして入れます。

(setq major-mode 'my-mode)

ついでに、モードラインのモード名フィールドも変えましょう。これは、変数 mode-name に文字列で設定します。

(setq mode-name "MY mode")

どちらも、今から書こうとするモードの名前を設定します。好きな名前で構いま せん。

キーマップの設定

「どのキーを押した時に、どの機能を呼び出すか」という対応表のことを Emacs ではキーマップと言います。C-n, C-p など、どのモー ドでもほぼ共通で使えるキーバインドはグローバルマップに、モードに固有のキー バインドは、ローカルマップに設定します。通常グローバルマップは変数 global-map常に使用します。(see section インタラクティブ関数をキーにバインドする)

メジャーモードが固有のキーバインドを使用する場合、独自のローカルマップを 作成し、そのローカルマップを使用する宣言をしなければなりません。ローカルマッ プを作成するには次のようにします。この例では変数 my-local-map をロー カルマップとしています。

(setq my-local-map (make-keymap))

これで、my-local-map というローカルマップが作成できました。あとは、 このマップに必要なキーバインドを定義して行くだけです。

キーの割当て

ローカルマップにキーを割当てるには、関数 define-key を使用します。 define-key は引数を三つ取り、順に、キーマップ、割当キー(文字列)、機 能(シンボル)となっています。では vi のように hjkl に左下上右 を割当ててみましょう。

(define-key my-local-map "h" 'backward-char)
(define-key my-local-map "j" 'previous-line)
(define-key my-local-map "k" 'next-line)
(define-key my-local-map "l" 'forward-char)

ローカルマップ使用宣言

では、今 my-local-map に割当てたバインド有効となるように、ローカ ルマップの使用宣言をします。

(use-local-map  my-local-map)

これで、カレントバッファで hjkl が有効になります。

メジャーモード関数の定義

インタラクティブ関数

メジャーモードを起動する関数を定義する前に インタラクティブ関数について 知っておきましょう。Emacs-Lisp の関数には、インタラクティブ関数と、 そうでない関数があります。C-nM-x gnus のようにキーボード操 作で直接呼び出すことができる関数をインタラクティブ関数と言います。それ以外 の関数は、ユーザが直接呼び出すことはできず、様々な処理の下請関数としてだけ 呼ばれます。C-f にバインドされている forward-char ももちろん インタラクティブ関数となっています。

メジャーモードの核となる関数は、当然インタラクティブ関数にしなければなり ませんから、関数定義は次のような形になります。

(defun my-mode ()
  (interactive)
  モード名の設定
  キーマップの設定
)

今のところは、「関数定義の先頭におまじない (interactive) を入れる」 と覚えておいて下さい。(see section 余談 2 関数・変数)

インタラクティブ関数をキーにバインドする

簡単なインタラクティブ関数を定義して、実際にキーに割当ててみましょう。バッ ファに `Hello, world!' とだけ表示する関数は次のようになります。

(defun hello-word ()
  (interactive)
  (insert "Hello, world!\n"))

すぐに試してみたいので、グローバルマップにキー割当してしまいましょう。

(define-key global-map "\C-ch" 'hello-world)

これで、C-ch を押すとバッファに `hello, world!' が挿入されます。

関数を実際に定義する

ここまでで、必要最低限の知識は全て揃いました。次のようなメジャーモードが 作成できるはずです。

実際の関数定義は、次のようになります。

(defun my-mode ()
  (interactive)
  (setq major-mode 'my-mode
    mode-name "MY mode")
  (setq my-local-map (make-keymap))
  (define-key my-local-map "h" 'backward-char)
  (define-key my-local-map "j" 'previous-line)
  (define-key my-local-map "k" 'next-line)
  (define-key my-local-map "l" 'forward-char)
  (define-key my-local-map "\C-ch" 'hello-world)
  (use-local-map my-local-map))

(defun hello-world ()
  (interactive)
  (insert "Hello, world!\n"))

章末問題 1

問 1-1

a-z どのキーを押しても、「僕るねえもんナリよ」が挿入される「るねきち モード」を作りなさい。

答の提出をもって、参加表明とみなします。よろしゅうに

あっという間に宿題を提出した優秀な人へ追加問題。

問1-2

a-z を押すと、対応するアルファベットで、「僕luneえもんAなりよ」〜 「僕luneえもんZなりよ」と、文字列の入る luneえもんモード を作成せよ。

暇な人だけ解いて下さい。

まとめ 1

大事なことを書き忘れたかもしれない。面倒なので、これは junk風の書き方で 許して。

@center{--- ここまで Emacs-Lisp mode ---}

これだけ知ってれば、あなたも今日から Emacs-Lisp プログラマ。では good luck!

余談 1 「〜関係の関数はないかな?」を探す。

関数を探す時に使うと便利なのが、apropos です。M-x apropos で出て来るプロンプトで、関数名の正規表現を入力します。何か文字列操作のため の関数を探したかったら、

M-x apr
Apropos: string

などと問い合わせると良いでしょう。正規表現での指定なので、`string' で 始まる関数を調べたかったら、`^string'という検索していも可能です。

ですから、今回の場合は、

M-x apr
Apropos: ^....-.......-....$

で、検索すれば良かったということになります。

Emacs-Lisp の構文

本章では、Lisp 言語とみた場合の変数の扱いや、制御構造の表現の仕方 について簡単に触れます。

変数

Lisp では変数は一つのシンボルとして存在しています。シンボルへの値 の代入は set によって行います。シンボル foo へ、値5を代入す るには、

(set 'foo 5)

としますが、一般的にはこれと等価な、

(setq foo 5)

という書式を用います。setq は値の代入なので、常に変数の値は更新され ます。

変数の宣言

defvarsetq とは異なり変数の宣言のみを行います。書式は

(defvar   シンボル 初期値 ドキュメンテーション文字列)

となっています。もし第一引数のシンボルが既に存在していた場合はその値は変更 しません。このため、Emacs-Lisp プログラムで使用するカスタマイズ可能 な変数のデフォルト値の設定をするためによく使われます。

変数の束縛

Emacs では多くの Emacs-Lisp プログラムが動作するため、シン ボル名の衝突は確実に回避しなければなりません。関数の内外にかかわらず単純に setqdefvar されたシンボルはすべてグローバル変数になって しまうので他のプログラムの存在を考えると好ましくありません。そこで変数の有 効範囲(スコープ)を制限するために let を用います。

(let (変数リスト) 実行部...)

「変数リスト」の部分は「変数名」または「(変数名 初期値)」の任意の個数の並 びです。変数名だけ指定するとその変数の値は nil にセットされます。次 の例は変数 case-fold-searcht にセットしてインクリメンタ ルサーチを呼び出します。

(let ((case-fold-search t))
  (isearch-forward))

case-fold-search は検索の時に大文字小文字を区別しないというフラグで、 グローバル変数となっています。let はグローバル変数の値を一時的に変 更するためにも利用できます。

しかしカスタマイズ変数などはグローバルに値を保持する必要があるので、グロー バル変数はやはり必要です。さらに、関数名はすべてグローバルシンボルとして扱 われます。そこで、グローバルシンボルを使用する時には、「すべてのグローバル シンボルには作成パッケージ固有の接頭辞をつける」ことが強く勧められています。 例えば `supercite' パッケージで用いるシンボルには全て sc- とい う接頭辞がついています。

制御構造

Emacs-Lisp で主に用いる制御構造を説明します。

if

if は第一引数を評価し、それが nil でない値(今後 non-nil)を返した場合第二引数を評価しその値を返し、nil だった 場合第三引数以降を (もしあれば)評価し、最後の値を返します。

(if CONDITION T-body Else-body...)

もし、条件が non-nil の時に評価したい関数が複数ある場合は、 progn を用いて次のようにします。

(if CONDITION (progn T-body...) Else-body...)

progn は任意個の引数を取り、最後の引数の値を progn の値とし て返します。

or

or は与えられた引数全てを順に評価し、non-nil を返すものが あった場合、それを or の返す値とします。もしすべての引数を評価した ものが nil だった場合、ornil を返します。

ある変数の値が t の時に動作が禁止される関数などは次のように表現し ます。

(or foo-key-map (setq foo-key-map (make-key-map)))

また、or のもつ「または」という意味で、次のように使うこともできます。

(if (or A B) 処理)

and

and は与えられた引数を順に評価し、nil を返すものが見つかっ たら直ちに nil を返します。最後の引数まで non-nil を返した場 合、and は最後の引数の値を返します。

cond

cond は次の書式によります。

(cond
 (式1  式1がnon-nilの時に返す式...)
 (式2  式2がnon-nilの時に返す式...)
    :
 (式n  式nがnon-nilの時に返す式...))

次の例は、変数 var-a, 変数 var-b, 関数 func-c の値 を順次調べて、どれかが non-nil の時に後続する関数群を評価します。も し、var-a, var-b, func-c のどれも nil を返す時 は、最後の条件ブロックの式が t なので最後のブロックを評価します。

(cond
 (var-a     (message "A!"))
 (var-b     (insert  "B!"))
 ((func-c)  (insert  "C"))
 (t     (message "NO!") (ding)))

なお、cond は途中の条件式が non-nil を返し後続するブロックを 評価したら、残りの条件ブロックは評価せずに抜けてしまいます。 cond の返す値は、(non-nil を返した)条件ブロックの最後の式が返す値です。

while

最初の条件式が non-nil である間、二番目以降の引数をくり返し評価し ます。

(while 条件式 実行部...)

これはループを形成する時に用いることができます。C の for または while のような繰り返しを行う時は、通常 let と組み合わせて次 のように用います。

(let ((i ?a))
  (while (<= i ?z)
    (insert i)
    (setq i (1+ i))))

上の例は、a〜z をバッファ中に挿入します。この例から想像が付くように、 Emacs-Lisp では文字コードを `?文字' で表現します。?aa の文字コードを示すので、97 と等価です。

while は常に nil を返します(最後は条件式が nil となっ て終了するから)。

catch, throw

catchthrow は対にして使います。catch の書式を見 ると、

(catch タグ 実行部...)

となっていて、「実行部...」のいずれかで「タグ」が throw される と直ちに catch を抜けます。throw

(throw タグ 値)

のように使用し、この時の catch の返す値は第二引数の「値」となります。 もし「実行部...」で「タグ」が throw されなかった時は「実行部 ...」の最後の値が catch の返す値となります。

この関数のペアは、終了のタイミングの予測できないループを表現する時に使う と便利です。例えば、バッファ末までに判定関数 foo を満足する行がある か調べる場合を考えます。catch, throw を使わずに書く場合は次 のようになるでしょう。

(let (found)        ;局所フラグ nil 初期値
  (while (and (not found)(not eobp))
    (if (foo) (setq found t)
      (forward-line 1)))
  (if found
      見つかった場合の処理
    見つからなかった場合の処理))

ループが回る毎にフラグ foundt でないことを調べているので、 少々無駄な気がします。これを catch, throw を使って書き直すと、

(if (catch 'found
      (while (not eobp)
    (if (foo) (throw 'found t)
      (forward-line 1))))
    見つかった場合の処理
  見つからなかった場合の処理)

となります。もし (foo)non-nil を返す行があった場合はルー プ内部で 'foundthrow されるので、catch 関数の値は 'found となり最も外側の if は見つかった場合の処理を評価しま す。逆に、 foo を満たす行が見つからなかった場合、内部の whilenil を返して終了するため、外側の if も、見つ からなかった場合の処理を評価します。

算術

演算子

Emacs-Lisp で使える演算子には以下のものがあります。

%, *, +, -, /
剰余, 乗算, 加算, 減算, 除算
1+, 1-
1加算, 1減算
<, <=, =, /=, >, >=
比較演算子(/=は≠)

Lisp では *, +, - は複数のオペランドを取ること ができます。- は引数が一つの時はその符号を反転し、二つ以上の時は一 つ目の引数から残りの引数全てを引きます。

また次の述語関数も必要でしょう。

numberp(integerp)
数値なら t を返す

独自に定義した関数で受け取った引数が数値かどうか判定する時などに利用しま す。

算術関数

利用頻度が高いと思われるものだけ紹介します。

max, min
引数のうち(最大,最小)のものを返す
random
24bits長の擬似乱数を返す引数としてt を与えると乱数の種を変えて値を 返す

章末問題 2

前問「るねきちモード」の a-z のキーバインドのうち、どれか一つのキー を押すと「自爆」と言ってバッファを消去する機能を付け加えよ。

ヒント: char-to-string, ding, message, erase-buffer

余談 2 関数・変数

基本的なことですが、変数は

var

と、単体で参照し、関数は

(func args...)

と括弧つきの形で参照します。

defun の形は次のようになっています。

(defun 関数名 (引数リスト) 関数定義)

引数に何もとらない時は空リストにして

(defun 関数名 () 関数定義)

とします。

さて、Emacs-Lisp の場合関数は必ず値を返します。関数の値は、その関 数中で最後に評価されたものの値となります。例えば次の関数の返り値は5となり ます。

(defun foo () 5)

したがって次の例では、変数 bar に値5が返ることとなります。

(setq bar (foo))

関数の返す値は、「最後に評価されたもの」であり「最後に書いてある式の値」で はないので注意して下さい。

(defun baz (arg)
  (if (< arg 0) (- arg) arg))

という例では、引数 arg が負の場合 (- arg) が、正の場合 argbaz の返り値となります。

余談 3 デバッギング

変数の値を途中で表示させたい時には message 関数を使うと便利です。 message 関数は C の printf のようなフォーマットが使えます。 例えば、途中で変数 foo の値を見たい時は

(message "foo = %d" foo)

とします。もし、次の message などがすぐに出てしまい読み取れない時な どは、sit-for 関数を使ってn秒間止まらせると良いでしょう。

(message "foo = %d" foo)
(sit-for 2)     ;2秒間停止
    :
(message "bar = %d" bar)

余談 4 プログラムの評価

defun した関数を評価する時は、ESC C-x を使うのが良いでしょ う。この時関数を定義したらその場で ESC C-x をしてしまいましょう。す るとその間数名は以後どの場所でも、ESC TAB によって補完することが可能 になります(変数名も同様)。

(defun lune-random ()
  なんちゃらかんちゃら)
(defun lune-mode ()
  (interactive)
  (setq key lun

というところで、ESC TAB を押すと、lune-random が補完されます。

さて出来上がった関数を評価する時は主に二つの方法が考えられます。

  1. ESC ESC で関数を評価するS式を入れる。
  2. *scratch* バッファに移ってS式を入れて C-j する。

上の lune-random 関数を試したい時は、

  1. ESC ESC Eval: (lune-random) RET
  2. *scratch*バッファに移動 (lune-random) C-j

(2)の場合、C-j を押す直前のS式が評価されます。したがって、

(setq random (lune-random))
                         ~~

の、最後の括弧の上で C-j を押すと (lune-random) だけが評価さ れ setq されず、行末で C-j を押すと setq 全体が評価さ れます。

最低限の関数をおさえる

どんなメジャーモードでも書けるようになるために、必要最低限の関数を一気に 覚えてしまいましょう。ここでとり上げる必要最低限のコマンドは以下のものです。

カーソル移動

カーソルの移動コマンドには、相対移動、絶対移動、検索移動があります。いず れもキーに割当てられている機能なので、関数名を知っているものもあるでしょう。

相対移動

Emacs-Lisp プログラムではあまり用いることのない関数ですが、各関数 と引数について説明します。

char 単位移動

forward-char, backward-char どちらも引数を一つ取り、移動桁 数を決定します。前に3つ進みたい時は、(forward-char 3) のように呼び 出します。もちろんこれらの関数は、C-f, C-b にバインドされてい ます。

word 単位移動

forward-word, backward-word も移動単語数を指定する引数を一 つ取ります。M-f, M-b にバインドされています。

line 移動

行移動というと、C-n, C-p にバインドされている next-line, previous-line を想像すると思いますが、これらの関 数は Emacs-Lisp プログラム中で用いません。これらの関数には goal-column の制御などの機能が含まれるため、プログラム中からはより 単純で信頼性の高い forward-line を使用します。なお、 backward-line という関数はないので、上に移動するには負の引数を渡し ます。

絶対移動

ポイント

カーソルの絶対移動の基準となる「ポイント」について知っておく必要がありま す。Emacs ではカーソルの位置をバッファの先頭からのオフセットで管理 しています。このオフセット値を返す関数が (point) です。これに関連し て、バッファの先頭は (point-min)、末尾は (point-max) で得る ことができます。

ポイント移動

指定するポイント位置に移動する関数が goto-char です。引数を一つ取 り、移動先のポイントを受け取ります。バッファの先頭に移動するには次のように します。

(goto-char (point-min))

なお、バッファの先頭にジャンプする M-< の関数名を知っている人は 「(beginning-of-buffer) でもいいんでしょ?」と思われるかもしれません が、これと (end-of-buffer) は特別な理由のない限り、 Emacs-Lisp プログラム中から利用してはいけません。これは、マーク位置 を変更してしまうため、ユーザに思わぬ動作を起こさせる可能性があるからです。

行移動

n行目に移動する関数として、goto-line があります。行番号引数として 渡します。なお、バッファの先頭は1行目と数えます。逆に、現在の行番号を得る には次のようにします。

(count-lines (point-min) (point))

桁移動

n桁目に移動する関数は move-to-column で、0から始まる桁数を引数と して渡します。現在の桁数は (current-column) で得ることができます。

また、行頭/行末への移動関数は、beginning-of-line, end-of-line です。この二つは比較的よく使われます。

移動境界の検査

相対移動絶対移動共に現在のポイント位置が移動できる終端まで来たかどうかの チェックをする必要がある場合があります。ポイント位置がバッファ中の特定の位 置にあるかどうかを検査する関数には以下のものがあります。

(bobp) / (eobp)
バッファ先頭/末尾
(bolp) / (eolp)
行頭/行末

関数 bobp/eobp は現在のポイント位置がバッファ先頭/末尾なら真(t) を、 そうでなければ偽(nil)を返します。バッファ末に達するまでなにかの処理を繰り 返すというケースはしばしば必要になります。次のような形で書けるでしょう。

(while (not (eobp)) ;(while 条件式 処理1...処理n)
  処理
  (forward-line 1))

絶対移動関係のまとめ

ここまでに出てきた関数をまとめましょう。

    ・ポイント値を返す関数  point, point-min, point-max
    ・移動関数              forward-char, backward-char
                            forward-word, backward-word
                            forward-line
                            goto-char, goto-line
                            move-to-column
                            beginning-of-line, end-of-line
    ・位置に関する述語関数  bobp, eobp, bolp, eolp

検索移動

プログラム中でカーソル位置を決定するのに最も頻繁に使用するのが検索です。 ここは必ず押さえましょう。

検索は大別して、

に分けられます(分けます)。これらのうち、インクリメンタルサーチは対話的に用 いることを前提としているため、Emacs-Lisp プログラム中からは滅多に利 用することはないでしょう。これ以外のものの利用の仕方を説明します。

Lisp エスケープキャラクタ

検索関数には検索したいパターンを文字列として渡すわけですが、その文字列中 に \ を含む場合は注意が必要です。\Emacs-Lisp で扱 う文字列中で特別な意味を持つエスケープキャラクタとなっています。 C言語の文 字列中で使う \n のような働きを持っています。主なシーケンスには次の ものがあります。

\\
\自身
\C-英字
コントロールコード(\^英字 も可)
\e
ESC(1Bh)
\"
"
\n
改行文字(LF)
\r
復帰文字(CR)
\a
ベル(C-g)
\b
バックスペース(C-h)
\f
改頁文字(C-l)
\t
タブコード(C-i)

Cを扱ったことのある人は「\\ とかなら慣れてるから平気」と思われる かもしれません。しかし、正規表現の検索パターンを指定する場合には、正規表現 での \ エスケープと、Lisp\ エスケープが重なってし まうので非常に繁雑です。正規表現検索で、\ 自身を探すにはパターン文 字列として、\\\\を指定することになります。

このような注意点があることだけを念頭において検索関数の理解に進みましょう。

文字列検索

探したい文字列がはっきりとわかっている場合には普通の文字列検索である search-forward, search-backward を利用します。これらの関数は 引数を一つから四つ取ります(二つ目以降は省略可)。

(search-forward 文字列 範囲 エラー処理 繰り返し回数)

各引数について説明します。

`1.文字列'
検索したい文字列.
`2.範囲'
どこまで検索するかをポイント位置で指定する. バッファ末までの時は nil を指 定する.
`3.エラー処理'
見つからなかった場合の処理を指定. エラーを発生して欲しい時は nil を、単に nil を返して欲しい時は t を、検索範囲末まで移動して 欲しい時は nil, t 以外を渡す.
`4.繰り返し回数'
この引数で指定した回数だけ検索を繰り返す.

後述する正規表現検索も同数の引数を取りますが引数の意味として違うのは、第一 引数だけです。もちろん正規表現検索関数の第一引数は正規表現のパターン文字列 を指定します。

search-forward/backward を用いた典型的な処理形態は次のようになり ます。

(if (search-forward "文字列" nil t)
    (progn 見つかった場合の処理)
  見つからなった場合の処理)

単語検索

`TeX' を検索する時に `LaTeX' にはマッチして欲しくない時のよう に、単語単位での検索に有効なのが word-search-forward(backward) です。 引数は search-forward の第一引数を単語に置き換えたものです。

正規表現検索

re-search-forward, re-search-backward がおそらく最もよく用 いる検索関数となるでしょう。第一引数に検索したい正規表現パターンを指定しま す。Emacs-Lisp で扱える正規表現全てについてはほかの解説書に譲ります。 ここでは必要最低限のものに絞ってメタキャラクタの説明をします。

代表的なメタキャラクタ

.
任意の一文字にマッチ
*
直前の正規表現の0回以上の繰り返し
+
直前の正規表現の1回以上の繰り返し
?
直前の正規表現の0回か1回の繰り返し
^
行頭にマッチ
$
行末にマッチ
[文字リスト]
「文字リスト」のうち一文字にマッチ
[^文字リスト]
「文字リスト」にないものにマッチ
[X-Y]
ASCIIコードが「X」のものから「Y」のものどれかにマッチ
[-^A-Z]
「-」か「^」か「A〜Zのうちどれか」

これだけ覚えておけば、ほとんどの検索が可能です。なお、正規表現の検索と組み 合わせた処理は極めて重要なので、別に節を設けて解説します。

文字(種)スキップ

単語の先頭にポイントが位置する時に、単語末までポイントを移動したい、ある いはその逆のことをしたい時などに skip-chars-forward, skip-chars-backward が利用できます。これらの関数は一つまたは二つの 引数を取ります。

(skip-chars-forward スキップ文字リスト スキップ境界)
        1.スキップ文字リスト    正規表現の[]の中味と同様に指定し
                                ます。
        2.スキップ境界          文字スキップを行う境界をポイント
                                値で指定します。これを越えてポイ
                                ントが進むことはありません。

英単語の先頭にポイントがある時に、単語末までポイントを移動するには次のよ うにします。

(skip-chars-forward "A-Za-z")

検索結果へのアクセス

次の例で `def' を検索した場合の検索後のポイント位置は

abc def ghi
    ~  ~

前方向検索の時は `def' の次の位置、後ろ方向検索の時は `d' の位置 になります。これはインクリメンタルサーチなどを前後方向で行った場合のカーソ ル位置と同じなので、容易に想像がつくことでしょう。

しかし、検索がマッチした部分をアクセスする時に、検索後のポイント位置を当 てにしていたのでは、検索方向によって場合分けしなければならないので通常はこ れを利用しません。検索のマッチ部分を取得する関数が match-beginning, match-end です。上記の例の、マッチ部分の始まり(dの位置)と、終わり(f の次の位置)はそれぞれ、

(match-beginning 0)
(match-end 0)

で得ることができます。どちらも引数として数値である0を渡しています。実はこ の部分は、正規表現にグループを用いた場合のグループ番号を意味していて、 0 は「マッチした部分全体」という特殊な意味を持っています。例えば上記の例を次 の正規表現で検索した場合を考えてみましょう。

(re-search-forward
  "\\(a.*\\) *\\(d.*\\) *\\(g.*\\)" nil t)

Emacs の正規表現のグルーピングは \(グループ\) で行いますが、 Emacs-Lisp\ を正規表現関数に渡すためには \\ と表 記しなければならないことに注意して下さい。さて、この正規表現の意味は、

・`a'で始まる任意の文字列(これをグループ1とする)のあとに
  `d'で始まる任意の文字列(これをグループ2とする)と
  `g'で始まる任意の文字列(これをグループ3とする)が続く

となります。

検索関数のまとめ

・検索関数          search-forward, search-backward
                    word-search-forward, word-search-backward
                    re-search-forward, re-search-backward
・文字スキップ      skip-chars-forward, skip-chars-backward
・検索結果位置取得  match-beginning, match-end

練習問題 3-1

先のるねきちモードにおいて、a を押した時に既にバッファ中に存在す る「僕るねえもん `A_n' なりよ」を数え、その数に応じて「僕るねえもん `A_(n+1)' なりよ」を挿入する用に書き換えよ。

すなわち、b を押した時にバッファ中に「僕るねえもん `B' なり よ」がいた場合は「僕るねえもん `B2' なりよ」を、c を押した時 にバッファ中に「僕るねえもん `C' なりよ」、「僕るねえもん `C2' なりよ」、…、「僕るねえもん `C10' なりよ」がいた場合は「僕るねえも ん `C11' なりよ」を挿入する。

ヒント:
        point, re-search-*ward, \\(\\), buffer-substring
        match-beginning, match-end, string-to-int

    ・全てのマッチする文字列に対して処理

        現在位置を保存
        先頭へ
        (while (re-search-forward パターン nil t)
          処理)
        処理
        位置を復帰

ポイント:
        ・グローバル変数は避けましょう。
        ・A1, A2, A5 なんて時はどうしましょうかね?
          適当に仕様を決めて下さい(A3とかA6とか)。

ただ、これによってアルゴリズムがかなり変わる↑

ポイント位置の保存

カーソル移動のための関数を書く場合を除き、Emacs-Lisp 中でポイント 移動を行った場合には、ユーザのその後の編集の事を考慮し、ポイント位置を復帰 しておく必要があります。そのための関数が save-excursion です。

(save-excursion 実行部...)

のようにポイント移動を伴う部分を save-excursion の中に閉じこめるこ とにより、「実行部...」でいかなる場所にポイントを移動しようと、 save-excursion を抜けると同時に、ポイントは元の位置に復帰します。さ らに、マーク位置も保存されるので、「実行部...」でマーク位置にアクセス する関数を書いた場合も、ユーザのその後の編集操作に支障を来しません。

次の例は、ポイントのある行を kill-ring に入れつつ二重化します。

(defun duplicate-line ()
  (interactive)
  (save-excursion
    (beginning-of-line)
    (copy-region-as-kill (point)
             (progn (end-of-line) (point)))
    (forward-line 1)
    (yank)))

関数中でポイントを移動していますが、実行が終わると関数起動時のポイント位置 に復帰します。

文字列挿入/削除

文字列挿入

既にメジャーモードの練習関数で、文字列を挿入する関数 insert は使 用済みです。ほとんどの文字列操作は insert 関数で用が足りますが、次 のものを知っておくと便利な場合が有ります。

insert-char

同じ文字をたくさん入れたい時に使用できます。引数を二つ取り、最初の引数は 文字コード、二つ目の引数は個数です。文字 `a' を100個入れたい時は次の ようにします。

(insert-char ?a 100)

self-insert-command

A-Z, a-z, 0-9, など一般のキーに割当てられている関数が これです。define-key などでキーに結び付けられた関数中で、押したキー そのものを挿入したいときにこの関数を利用します。この関数も、繰り返し挿入回 数を指定する引数を取るので通常は(self-insert-command 1) のように呼 び出します。(see section キーの割当て)

ちなみに、キーバインドされた関数から、その関数が起動されたキーを知るた めには、関数(this-command-keys)を参照します。次のような関数をいろい ろなキーに割当てて実行してみるとおもしろいでしょう。

(defun show-my-key ()
  (interactive)
  (insert (this-command-keys)))

文字列と他の型の変換

format

バッファ中に挿入したいのは文字列だけとは限りません。なにかの計算によって 選られた数値を文字列化して挿入したいことがあります。このような時に用いるの が format 関数で、Cの printf でのフォーマットとほぼ同じもの が利用できます。ここで Emacs-Lisp で扱うことのできる型には、以下の ものがあります。

`シンボル'
'foo, 'bar
`数値(整数)'
1, 2, 3, -50, 65537 (24bits)
`char型'
0 〜 127
`文字列'
"foo", "こんにちは"

これらの型のものが単体で用いられる場合、それを「アトム atom 」と言 います。逆に様々な型のアトムが集合したものに、「リスト list 」と 「配列 array 」があります。これらの概念については、一般の Lisp の参考書などを見ると説明が載っています。それを理解していると複 雑な処理が効率的に書けるようになることがあるかもしれませんが、とくに理解し ていなくてもメジャーモードの作成には支障ありません。余裕ができたら覚えましょ う。(see section リスト)

さて、関数 format には、すべての型の値を文字列に変換するためのキー ワードが三種類有ります。

%s
シンボル、または文字列
%d,%o,%x
数値(10,8,16進数表示)
%c
char型数値を文字コードとみなし文字を表示

format 関数は第一引数に上記の `%' を含むフォーマット文字列を、 第二引数以降に文字列中の `%?' に対応する変数/定数を受け取ります。そし て、それらの引数を文字列化したもので元の `%?' を置き換え、すべて置き 換えることで出来上がった文字列を返します。

これを用いて各種の値を表示させてみます。

(setq   foo 50
    bar ?x
    baz "hoge")
(insert (format "%d, %o, %x  %s   %c  %s\n"
         foo foo foo 'foo bar baz ))

format 関数の第一引数中、`%?' が文字列に変換される様子は次のよ うになります。

%d  →  "50"
%o  →  "62"
%x  →  "32"
%s  →  "foo"
%c  →  "x"
%s  →  "hoge"

従って format 関数が返す文字列は

``50, 62, 32  foo   x  hero''

となります。

型変換

バッファ中に存在する数値文字列を読み込みその値をもとになにかを計算し結果 を返すという処理を想定してみましょう。必要な処理内容は以下のものとなります。

ここでは、これらの処理に必要な関数をすべて覚えてしまいましょう。

文字列の取り込み

バッファの内容を文字列として返す関数は buffer-substring です。こ の関数は非常によく使うので、いやでも覚えてしまうでしょう。

(buffer-substring ポイント値1 ポイント値2)

第一引数と第二引数の間の内容を文字列として返します。通常この関数は、検索結 果を保持している関数 match-beginning, match-end と共に用いら れます。(see section 検索結果へのアクセス)

例として

Bytes: 67 Date : 10:23pm  6/28/93 Author:net66331 (luneえもん)

という行から時刻を抽出する関数を定義してみましょう。そのためには、このフォー マットで書かれている行を表現する正規表現を考える必要があります。簡単のため、 ここでは「行頭が Bytes: で始まり、時刻文字列があり、(ハンドル)で終 わる行」というものにします。これをそのまま正規表現にすると、

^Bytes:.*[0-9 ][0-9]:[0-9][0-9][ap]m.*(.*)$
         ~~~~~~~~~~~|~~~~~~~~~~|~~~~

となるでしょう。しかし今回の場合時刻を取り出したいので、この部分をグループ 化して、

^Bytes:.*\([0-9 ][0-9]\):\([0-9][0-9]\)\([ap]m\).*(.*)$

とします。検索が成功した場合、(match-beginning 1)(match-end 1)`時' の部分の先頭と末尾のポイント値が入るはず です(以下も同様)。これを Lisp 中に書くときは \\\ でエス ケープすることを忘れないようにしましょう。

(defun access-time ()
  (interactive)
  (re-search-forward
   "^Bytes:.*\\([0-9 ][0-9]\\):\\([0-9][0-9]\\)\\([ap]m\\).*(.*)$"
   nil t)
  (message
   (concat
    (if (string= (buffer-substring (match-beginning 3) (match-end 3))
         "am")
    "午前" "午後")
    (buffer-substring (match-beginning 1) (match-end 1)) "時"
    (buffer-substring (match-beginning 2) (match-end 2)) "分")))

関数 string= は文字列どうしが等しいかどうかを比較します。この例では、 グルーピングした3番目の部分、つまり ampm の部分が、 am だったら `午前' を返し、そうでなかったら `午後' を返し ています。さらに、グルーピングの1番目と2番目、つまり「時」と「分」の部分に それぞれ `時'`分' を添えています。そしてそれらを concat で全て結合したものを message 関数に渡しています。

次の節に進む前に、もう少し分かり易く書き直しておきましょう。全く同じ動作 をします。

(defun access-time ()
  (interactive)
  (re-search-forward
   "^Bytes:.*\\([0-9 ][0-9]\\):\\([0-9][0-9]\\)\\([ap]m\\).*(.*)$"
   nil t)
  (let((h  (buffer-substring (match-beginning 1) (match-end 1)))
       (m  (buffer-substring (match-beginning 2) (match-end 2)))
       (ap (buffer-substring (match-beginning 3) (match-end 3))))
    (message
     "%s%s時%s分" (if (string= ap "am") "午前" "午後") h m)))

数値変換

数値を表わす文字列を実際の数値に変換するための関数は、 string-to-int です。

(string-to-int "数値文字列")

もし、文字列が数値として意味のない文字列である場合は0を返します。では、早 速この関数を使って先程の例を24時間制で表示するように書き換えてみましょう (展開が予想できましたね?)。先の例では、hm に時刻を表わす 数値文字列が入っているので、これを string-to-int で数値に変換し、も じ `pm' だったら「時」に12を足しましょう。

(defun access-time ()
  (interactive)
  (re-search-forward
   "^Bytes:.*\\([0-9 ][0-9]\\):\\([0-9][0-9]\\)\\([ap]m\\).*(.*)$"
   nil t)
  (let*((h  (buffer-substring (match-beginning 1) (match-end 1)))
        (m  (buffer-substring (match-beginning 2) (match-end 2)))
    (ap (buffer-substring (match-beginning 3) (match-end 3)))
    (hour (string-to-int h))
    (min  (string-to-int m)))
    (if (string= ap "pm") (setq hour (+ 12 hour)))
    (message "%d時%d分" hour min)))

新しい形 let* が出てきました。let との違いは、変数の初期化に それ以前のローカル変数の値を利用できる点です。上の例では、変数 hour の初期化に `h' の値を利用しているので、let* を使う必要がありま す。

余談となりますが、文字コードを返す関数として、string-to-char があ ります。これは、引数として与えた文字列の先頭の一文字の文字コードを返します。

文字列変換

今回の例では %s による(出力時の)文字列への変換を用いたので、数値 →文字列(変数間)の型変換は用いませんでしたが、string-to-int の逆の 仕事をする int-to-string という関数があります。必要に応じて利用する と良いでしょう。

文字列操作関数

ついでに文字列に対する種々の操作関数を覚えておきましょう。 M-x aproposstring をキーに探せばいろいろ出てきますが、ここでは主な ものを取り上げます。

(string-equal "文字列1" "文字列2")  ;string= と同じ
(string< 文字列1 文字列2)
(string> 文字列1 文字列2)           ;文字列の大小比較
(string-match 正規表現 文字列)      ;第一引数の正規表現が第二引数の文
                                    ;字列中の何文字目にマッチするか。
                                    ;マッチしなければ nil
(stringp 変数)                      ;変数の値が文字列かどうか
(substring 文字列 開始 終了)        ;文字列の「開始」〜「終了」の部分
                                    ;文字列。第三引数を省略すると開始
                                    ;位置から文字列末尾まで。位置を負
                                    ;で与えると文字列の後ろから数える。

文字列削除

バッファの一部を削除する関数で Emacs-Lisp 中から主に用いるのは以 下のものでしょう。

delete-chardelete-backward-char は引数として削除する文字 数を指定します。しかし複数文字を削除する時は通常 delete-region を用 います。

(delete-region 削除開始ポイント値 削除終了ポイント値)

手で編集する時の関数 kill-region は、kill-ring を変えてしま うので、Emacs-Lisp 中から利用してはいけません (1)

なにかのパターンを検索して該当部分を削除するということが多いので、 delete-regionmatch-beginning, match-end と共に用 いられることがほとんどです。

バッファ中に出現する特定のパターンを削除するというケースは非常に多くあり ます。たとえば、

[Continued] 

というパターンを全て削除するコードは以下のようになります。

(defun kill-more ()
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward
    (delete-region (match-beginning 0) (match-end 0))))

このような、

(while (検索関数 パターン nil t)
  (delete-region (match-beginning ??) (match-end ??)))

という関数の組み合わせは特定のパターンを全て削除する時の定石として覚えてお きましょう。

文字列置換

単純な文字列の置換は、対話的に済ませることが多いのであまりプログラムでは 必要とはなりません。やはり、正規表現検索と組み合わせる ことが多くなります。 特定のパターンを一括置換する場合は次のように なります。次の例は、コントロー ルコードの ^L`山記号'L に置換します。

(defun replace-C-l ()
  (interactive)
  (goto-char (point-min))
  (while (search-forward "\C-l" nil t)
    (replace-match "^L")))

この例の場合は、マッチした部分全体を置換していますがそのような時に使う関数 が replace-match で、seds/old/new/ の後半にあたり ます。

(replace-match  置換文字列
        大小文字を保存するかのフラグ
        \を特別扱いしないかのフラグ)

第二引数以降は省略可能です。また \ を特別扱いしないフラグをセットし ない場合は「置換文字列」の部分に次の表記が利用できます。

\&
直前の検索でマッチした部分全体
\n
\(\) で指定したグループの内容(nは1-9)
\\
\ 自身

既にお気付きでしょうが、前節の一括削除は replace-match を使うと

(while (検索関数 パターン nil t)
  (replace-match ""))

と簡単に書くことができます。

バッファ編集関数のまとめ

・文字列挿入        insert, insert-char, self-insert-command
                    this-command-keys(関連)
・文字列の取り込み  buffer-substring
・型変換            format, string-to-int, int-to-string
                    string-to-char, char-to-string
・文字列比較等      string=, string<, string>, string-match
                    stringp, substring
・削除              delete-char, delete-backward-char
                    delete-region, erase-buffer
・置換              replace-match

余談 5 雑関数

既に多くの人が利用しているので、次の関数は既習としましょう。

(sleep-for 秒数)
「秒数」だけ休止する。
(sit-for 秒数)
「秒数」だけ休止する。
ただし、既にキー入力が行われていた場合は先に進む。
(ding)
beep音を鳴らす。

余談 6 Emacs-Lisp のスコープ

Emacs-Lisp ではダイナミックスコープ(動的スコープ)を採用しています。 これは、C言語などのスタティックスコープとは異なり、実行時に参照する変数の 実体が決定するものです。具体例を見てみましょう。次のプログラムの実行結果を 予想し、実際に確かめて見て下さい。

/*---- C言語 ----*/
char *s = "外側のs";
sub()
{
    printf("sub: s = %s\n", s);
}
main()
{
    printf("main(外): s = %s\n", s);
    {
    char *s = "mainの中のs";
    printf("main(中): s = %s\n", s);
    sub();
    }
    sub();
}
;;; -*- Emacs-Lisp -*-
(defvar s "外側のs")
(defun sub ()
  (insert (format "sub: s = %s\n" s)))
(defun main ()
  (insert (format "main(外): s = %s\n" s))
  (let ((s "mainの中のs"))
    (insert (format "main(中): s = %s\n" s))
    (sub))
  (sub))

C言語では、あらゆるシンボルのスコープ(通用範囲)は、コンパイル時に決定され ます。先程のCのプログラムでは、次の図のような入れ子構造のスコープが形成さ れています。

+-----------------globalな箱----------------------+
|    char *s = "外側のs";                         |
|+---------------- subの箱 ----------------------+|
||   sub()                                       ||
||   {                                           ||
||       printf("sub: s = %s\n", s);             ||
||   }                                           ||
|+-----------------------------------------------+|
|+---------------- mainの箱 ---------------------+|
||   main()                                      ||
||   {                                           ||
||      printf("main(外): s = %s\n", s);         ||
||+----------- local-blockの箱 -----------------+||
|||      {                                      |||
|||          char *s = "mainの中のs";           |||
|||          printf("main(中): s = %s\n", s);   |||
|||          sub();                             |||
|||      }                                      |||
||+---------------------------------------------+||
||       sub();                                  ||
||   }                                           ||
|+-----------------------------------------------+|
+-------------------------------------------------+

それぞれの箱の壁は、内側から外側しか見ることのできないマジックミラーになっ ていると考えると分かり易いかもしれません。なにかのシンボルが参照されている 場合、もしその箱の内部でそのシンボルが宣言されていた場合(例えば local-block の箱の中のs)、そのシンボルが最優先で結合されます。 逆に箱の内部でシンボルが宣言されていない場合 (例えば subの箱の s)、コンパイラは箱をどんどん外側に見ていき、見つかった場合そのシン ボル(ここでは global な箱に存在する s)と結合します。

つまり sub() では、変数 sglobal な箱で宣言され ている s と結合され、これは sub() がいつ何時どこから呼ばれよ うと変わることはありません。常に `外側のs' の格納されているアドレスを 差しています。

スタティックスコープでは、シンボルとその実体との結合は、シンボルの参照が 行われている箇所の、ソースプログラムでの位置によって決定されます。

ところが、ダイナミックスコープでは、変数とその実体との結合は実行時に行わ れます。先程の Emacs-Lisp プログラムの例を評価順に追って考える必要 があります。

(defvar s "外側のs")                            ;これはロード時に評価される
→main
  (insert (format "main(外): s = %s\n" s))      ;外側のsが有効
  (let ((s "mainの中のs"))                      ;ここでローカルなsが発生
    (insert (format "main(中): s = %s\n" s))    ;let中のsは"mainの中のs"
    (sub)                                       ;letを抜けると同時に
  )                                             ;ローカルなsは消滅
  (sub)                                         ;外側のsが再び有効
→main 終わり

極端な例として、次のものの評価を追ってみると良いでしょう。

(defun hoge ()
  (message "x = %d" x))
(defun foo ()
  (let ((x 1)) (hoge)))
(defun bar ()
  (let ((x 2)) (hoge)))
(defun baz ()
  (hoge))

(foo), (bar), (baz) と順に評価してみて下さい。

ダイナミックスコープの性質を利用すると、ある関数から下請関数を呼ぶする場 合、引数の受け渡しを省略することができます。

(defun natural-rand (n)
  (let ((r (random t)))
    (abs-r)
    (% r n)))
(defun abs-r ()
  (if (< r 0) (setq r (- r))))

しかし、このような利用法は関数の汎用性を損なうだけでなく、どの変数を参照 しているのかが分かりにくく、可読性を落とすことになるので、特殊な処理でスピー ドを重視するようなもの以外では、利用しない方が良いでしょう。

練習問題 3-2

(1),(2)の好きな方を作成せよ。

選択問題(1)

ASCII-NET のログを解析し、直前の書き込みとの時間的間隔が一番大 きい書き込みを発見せよ。

つまり、以下のような書き込みがあった場合、

|Bytes: 3001 Date :  6:46am  7/12/93 Author:net92851 (ほんまたける)
|   やっと出来ました。いまから学校か。結構辛い!
|
|Bytes: 36 Date : 12:14pm  7/12/93 Author:pcs39334 (はすらあ)
|   おおついに!
|
|Bytes: 33 Date :  3:19pm  7/12/93 Author:net66331 (luneきち)
|   バイトはどうなさったのでしょう

      の場合、6:46am→12:14pm→3:19pm
                   5h28m    3h05m
    なので、二番目の書き込みが該当する。

ヒント:
        match-beginning, match-end, buffer-substring, string-to-int

選択問題(2)

先のるねきちモードのメッセージ「僕るねえもんXyyなりよ」はちょっと 長いので、「るねXyyなりよ」に変更し、次の機能を付加せよ。

「るねXyyなりよ」をたくさん表示させた状態で、

  1. 2,4,6,8を押すと、それぞれ 下/左/右/上 の [A-Z][0-9]* (以後これを「るね番号」と呼ぶ)に移動。
  2. スペースキーを押すと、
    1. 行末かバッファ末なら (self-insert-command)
    2. るね番号 の上なら、
      1. 直前に押したキーが 26 なら、行末までのすべてのるね番号を「自爆」に 置換する
      2. 直前に押したキーが 24 なら、行頭までのすべてのるね番号を「自爆」に 置換する
    それ以外なら (call-interactively 'fill-paragraph)
ヒント:
        cond, looking-at, (substring (recent-keys) 負の数),
        string=
        「delete-region & insert」または「replace-match」
        (註: 関数 recent-keys は最近押されたキーを文字列として返す)

  余裕があれば、

(1')2,4,6,8 のキーは一回押しただけでは動かず、二
回目以降から動く。
(つまり (recent-keys) の末尾二文字が同じ時に動く) かなり暇なら、 るね番号を縦に結んだ線がなんとなく揃うように fill-column を調整する。 (b3)直前キーが 626 なら、一つ右のるね番号に移動してからその真上にある (8で移動できる)るね番号全てを「自爆」に置換 (b4)直前キーが 424 なら、一つ左のるね番号に移動してからその真上にある (8 で移動できる)るね番号全てを「自爆」に置換 なお、以後これを「るねきちモードII」と呼ぶ。

補完入力とその周辺

Emacs の持っている機能のうち最も強力なものの一つが文字列やファイ ル名の補完入力で、入力支援のためのメジャーモードには必須の機能と言っても過 言ではないでしょう。本章では、補完入力機能を実装するために必要な知識とその 方法について説明します。

一般入力関数

補完入力関数の前に、通常の入力関数について説明します。文字列入力は、 read-string という関数によって行います。

(read-string プロンプト文字列 [初期入力])

第一引数の文字列をプロンプトとして出し、ミニバッファから文字列を読み込んで その結果を返します。この時に第二引数を与えると、それを読み込み時に既に入力 されていた文字列であるかのようにミニバッファに挿入します。

日本語文字列やスペースを含む文字列を読み込む場合などは補完が有効に働かな いので、read-string 関数が役に立ちます。次の例は、天候を読み込み日 付と共にバッファ中に挿入します。

(defun insert-date-weather ()
  (interactive)
  (insert
   (substring (current-time-string) 0 10)
   "\t"
   (read-string "Weather: ")
   "\n"))

もうひとつ、ファイル名を読み込む read-file-name を紹介します。第 二引数以降は省略可能です。

(read-file-name プロンプト文字列
        [ディレクトリ [デフォルト名 [要マッチ]]])

「ディレクトリ」はファイル名を入力するデフォルトのディレクトリ名を指定しま すが、これを省略するとカレントバッファの属するディレクトリとなります。「デ フォルト名」を指定すると、ユーザ自身が何も入力せずにリターンキーを押した場 合に、この値が read-file-name の結果として返されます。「要マッチ」 に t を指定した場合は実際に存在するファイル名以外の入力を認めません。 t でも nil でもない値を指定した場合は、補完入力の途中でリター ンキーを押した場合に本当にそのファイルでよいかどうかの確認をします。

補完入力関数

ミニバッファで補完入力を行う関数が completing-read です。

(completing-read プロンプト 補完テーブル 選択(述語)関数
         要マッチ 初期入力)

第一引数の「プロンプト」はミニバッファに出すプロンプト文字列、第四引数の 「要マッチ」は read-file-name のものと同様補完候補と必ず一致すべき かどうかを指定するフラグ、第五引数はミニバッファに最初から入力されている文 字列で、それぞれ特に説明の必要はないでしょう。

第二引数の「補完テーブル」はスペースキーやタブキーを押した時に補完される 単語を格納した変数です。このテーブルの構造は連想リスト (association list)(通称 alist)と呼ばれるもので、Lisp 言語では非常に良く 使われます。alist に限らず「リスト」は、Lisp の機能を最大限 に活かした Emacs-Lisp プログラムを書くためには必須の概念ですから、 この機会に覚えておきましょう。

リスト

Lisp で扱う対象の最小単位は「アトム」といい、今までに出てきた関数 や変数などの名前を表わす「シンボル」や、数値、文字列、tnil などは全てこれに属します。

「リスト」とは、「アトムまたはリスト、の集合体」です。Lisp ではア トム、リストを括弧で括って並べることで集合体を表現します。つまり、それぞれ のアトム foo, t, "bar" 5

(foo t "bar" 5)

のように並べたものがリストとなります。ではこれを変数 x にセットして みましょう。

(setq x (foo t "bar" 5))        ;×間違い

これでは期待通りになりません。(foo ...) という形は、「関数foo の 評価」という意味なので、Lisp インタプリタは関数 foo に幾 つかの引数を渡した結果を x に代入しようとします(たいていは未定義エ ラーとなるでしょう)。リストの形で渡したい時は、次のように ' をつけてクォー トする必要があります。

(setq x '(foo t "bar" 5))

' は、次のオブジェクトを評価しない、つまり「後続するものを変数や関数の参 照だと思わずにそのまま渡してくれ」と Lisp インタプリタに指示する働 きを持っています。

なお、本稿ではリストを表記する時は、関数評価との混乱を避けるため、' でクォー トして表わすことにします。

リストの構造

car, cdr, cons, list, append などのリ スト処理関数を覚える時には、リストがどういう構造で格納されているかについて 理解しておくと非常にスムーズに関数の働きが理解できます。(see section リスト作成)(see section リストの要素の参照)(see section リストの要素の追加)(see section リストどうしの結合)

リスト中の各要素は「コンスセル」と呼ばれる記憶領域に格納されます。たとえ ば前述のリストの例 '(foo t "bar" 5) はみかけから分かるように四つの 要素から成っていますが、これらの各要素はそれぞれコンスセルに格納されていま す。コンスセルは次のような構造をとっています。

        +--------------+--------------+
        |              |     次の     |
        |     要素     | コンスセルの |
        |              |   アドレス   |
        +--------------+--------------+

これにしたがって '(foo t "bar" 5) の格納されている様子を図にしてみ ましょう。記号Λは、それが最後のコンスセルであることを意味します。通常はΛ を表わす Lisp シンボルとしては nil が入っています。

        +---------+---------+      +---------+---------+
        |         |         |      |         |         |
        |   foo   |    *----+----→|    t    |    *    |
        |         |         |      |         |    |    |
        +---------+---------+      +---------+----+----+
        +---------+---------+  +-----------------/
        |         |         |  |   +---------+---------+
        |    5    |   Λ    |  |   |         |         |
        |         |         |  +-→|  "bar"  |    *    |
        +---------+---------+      |         |    |    |
             ↑                    +---------+----+----+
              \----------------------------------/

(setq x '(foo t "bar" 5)) はシンボル x にこれらのリスト(コン スセルの連結)の先頭のアドレスを代入します。

        +--x--+
        |  *  |
        +-----+
           ↓
        +-----+-----+  +-----+-----+  +-----+-----+  +-----+-----+
        | foo |  *--+→|  t  |  *--+→|"bar"|  *--+→|  5  | Λ  |
        +-----+-----+  +-----+-----+  +-----+-----+  +-----+-----+

また、概念的に、'(foo t "bar" 5) の各要素を次のように把握してお くのも良いでしょう。

        '(foo              t          "bar"           5)
          ↓              ↓           ↓            ↓
         fooと            tと         "bar"と         5と
        '(t "bar" 5)      '("bar" 5)      '(5)            Λ
          へのポインタ    へのポインタ   へのポインタ    のペア
            のペア          のペア         のペア

次に、リストの要素にリストがある場合について考えてみましょう。 nil と、これまで例として用いた '(foo t "bar" 5)hoge からなるリストは次のように表現できます。

'(nil (foo t "bar" 5) hoge)

リストの要素として、リストをそのままの形で書くだけで良いのです。

もう一つ、要素が一つもないリスト「空リスト」の存在も知っておく必要があり ます。空リストはリストの要素に何もないので、リストを括る括弧の中に何も書か ずに '() と表現します。空リストは常に nil を値として持ってい ます。Emacs-Lisp では空リストと nil は全く同じ意味を持ちます。

さて (setq x '(foo t "bar" 5)) について理解しておくべき重要な 事項は次のものです。

リスト処理関数

リスト作成

これまでは、リストの内容そのものを ' でクォートして並べましたが、 各要素を列挙してそれらから構成されるリストを作成することができます。関数 list がそれです。引数は任意の個数だけ書けます。

(list 'foo t "bar" baz)

のように書くことで、`foo t "bar" [bazの値]' からなるリストを作成し、 このリストへのポインタを返します。引数を全て評価したものを連結するので、 baz`シンボル baz' ではなく `baz の値' になることに注 意して下さい。例えばこの場合、(setq baz 5) としていた場合、

'(foo t "bar" 5)

というリストが生成されます。

リストの中にリストがある場合も、要素の部分に list 関数で内側のリ ストを書けば良く、前述の

'(nil (foo t "bar" 5) hoge)

(list nil (list 'foo t "bar" baz) 'hoge)

で作成することができます。

リストの要素の参照

要素の連結した形であるリストから、要素そのものを取り出す時に利用する関数 に car(かあ), cdr(くだー), nth(えぬす) があります。

リストは複数のコンスセルから成っていますが、carcdr は引数 として与えられたリストの先頭のコンスセルの、要素部分(car部)とポ インタ部分(cdr部)をそれぞれ返します。つまり、'(foo t "bar" 5)の 先頭のコンスセル(foo が入っているもの)は

        +--------------+--------------+
        |              | tの入っている|
        |     foo      | コンスセル   |
        |              | へのポインタ |
        +--------------+--------------+

となっているので、(car '(foo t "bar" 5))foo を返し、 (cdr '(foo t "bar" 5)) は次のコンスセルへのポインタ、すなわち t を先頭とするリスト '(t "bar" 5) を返します。 (see section リストの構造)

では、'(foo t "bar" 5)cdr をどんどん辿って行くとどうなるで しょう。次の例では、変数 xcdr の結果を入れて行きます。

(setq x '(foo t "bar" 5));x は '(foo t "bar" 5) ---(A)
(setq x (cdr x))         ;x は '(t "bar" 5)     ---(B)
(setq x (cdr x))         ;x は '("bar" 5)       ---(C)
(setq x (cdr x))         ;x は '(5)             ---(D)
(setq x (cdr x))         ;x は 'nil             ---(E)

(A)〜(E)の代入により変数 x の指し示すものは次のように変動します。

      (A)            (B)            (C)            (D)      (E)  
    +--x--+        +--x--+        +--x--+        +--x--+  +--x--+
    |  *  |        |  *  |        |  *  |        |  *  |  |  *  |
    +-----+        +-----+        +-----+        +-----+  +-----+
       ↓             ↓             ↓             ↓       +→ nil
    +-----+-----+  +-----+-----+  +-----+-----+  +-----+-----+
    | foo |  *--+→|  t  |  *--+→|"bar"|  *--+→|  5  | Λ  |
    |     | (B) |  |     | (C) |  |     | (D) |  |     | (E) |
    +-----+-----+  +-----+-----+  +-----+-----+  +-----+-----+

carcdr を組み合わせると、リストの二番目三番目……の要 素を取り出すことができます。

(car (cdr '(foo t "bar" 5)))            ;t
(car (cdr (cdr '(foo t "bar" 5))))      ;"bar"
(car (cdr (cdr (cdr '(foo t "bar" 5)))))    ;5

しかしこの方法ではもっと後ろの要素を取り出すのが大変なので、そのような時は nth を使います。nth

(nth n番目 リスト)      ;「n番目」は0から始まる

のように用いることで、リストの「n番目」の要素を返します。

リストの要素の追加

(setq x (cdr x)) のようにすることで、リストの先頭を順次切り捨てて 行くことができます。これとは逆にリストに要素を追加する関数が、 cons です。

(cons 要素 リスト)

は、「リスト」に「要素」を追加したリストを返します。list 関数もリス トを生成して返しますが、list は引数全てを要素とするリストを新規に作 成して返すのに対し、cons は既存のリストの先頭に新たな一つの要素を追 加したリストを返します。それではリストの逆の手順をとりつつ '(foo t "bar" 5) を構築して行きましょう。(see section リストの要素の参照)

(setq x (cons 5 '()))       ;(setq x (cons 5 nil))と同じ (5)
(setq x (cons "bar" x))     ;x は ("bar" 5)
(setq x (cons t x))         ;x は (t "bar" 5)
(setq x (cons 'foo x))      ;x は (foo "bar" 5)

最後の行を評価する前の x は次のようになっています。

                       +--x--+
                       |  *  |
                       +-----+
                         ↓
                       +-----+-----+  +-----+-----+  +-----+-----+
                       |  t  |  *--+→|"bar"|  *--+→|  5  | Λ  |
                       +-----+-----+  +-----+-----+  +-----+-----+

(cons 'foo x) により、cons はまず、'foocar 部とするコンスセルを新たに作成します。そして、その cdr 部にそれまで x が指していたリストへのポインタを格納します。上の図は次のように変 化します。

                       +--x--+
                       |  *  |
                       +-----+
                         ↓
        +-----+-----+  +-----+-----+  +-----+-----+  +-----+-----+
        | foo |  *--+→|  t  |  *--+→|"bar"|  *--+→|  5  | Λ  |
        +-----+-----+  +-----+-----+  +-----+-----+  +-----+-----+
        \________________これ全体が(cons 'foo x)________________/

そして最後に (setq x (cons 'foo x)) により x(cons 'foo x) で生成されたリストを指し示すこととなります。

        +--x--+
        |  *  |
        +-----+
           ↓
        +-----+-----+  +-----+-----+  +-----+-----+  +-----+-----+
        | foo |  *--+→|  t  |  *--+→|"bar"|  *--+→|  5  | Λ  |
        +-----+-----+  +-----+-----+  +-----+-----+  +-----+-----+

リストどうしの結合

たくさんの要素を一つのリストに組み上げる list、一つのリストに一つ の要素を追加する cons は既に覚えました。つまり、「要素だけ」と「リ スト対要素」のリスト合成関数は覚えたので、「リスト対リスト」の結合関数 append を覚えましょう。append は引数として与えられたリストを 全て結合します。

(append リスト1 リスト2 リスト3 ... リストn)

のようにした場合、「リスト1」から「リストn」のコンスセルを順次コピーしそれ らの全てをつなげます。つまり、「リスト1」の最後のコンスセルのコピーの cdr部(nilが入っている)に「リスト2」の最初のコンスセルのコピー へのポインタを格納し、「リスト2」の最後のコンスセルのコピーのcdr部 に「リスト3」の最初のコンスセルへのポインタを格納し、...という手順を繰 り返すことで、「リスト1」から「リストn」までのすべての要素(コンスセル)が一 つの鎖に連結されます。そして append は連結されたリストを返します。

この append の動作が、cons のものとは少し違うことに気付い たでしょうか。cons では第二引数として与えられたリストを構成するコン スセルのいずれも変化していません。cons の第一引数の要素をcar 部とするコンスセルを生成し、そのcdr部 が第二引数のリストを指し示す ようにしただけにすぎません。これに対し append は第一引数から第 (n-1)引数までのリストのコンスセルをすべてコピーし直します。 append によって返されたリストは、どの部分も新たな領域に確保されたものです。

述語関数等

ある対象物がリストなのか、あるいはアトムなのかを判定するための関数はそれ ぞれ listp, atom です。

(listp 引数)
(atom 引数)

は「引数」が リスト/アトム である場合に t を返し、そうでない場合に nil を返します。

また、リストに要素がいくつ含まれるかを返す関数に length がありま す。length はリストの要素としてリストがあった場合はそれを一つと数え ます。つまり、

'(a b c)
'(1 '(a (b c) x) 4)

は、どちらも長さ3と数えます。length は、リストの他に、文字列や (こ こでは述べませんが)配列の長さを数える時にも利用できます。

連想リスト

association list

'(連想キー  値1  値2  ...  値n)

というリストが、一個以上集まってさらにリストになったもので、

'((連想キー1  値1..値n) (連想キー2 ..) ...)

という形式をとっているもののことです。このリストを利用して検索キーが一つだ けの簡単なデータベースを作ることができます。たとえば、

隆          波動拳、昇龍拳、竜巻旋風脚
拳          昇龍拳、波動拳、竜巻旋風脚
春麗        スピニングバードキック、百烈キック
E.本田      スーパー頭付き、百烈張り手

という必殺技データベースを作りたい時に、

(setq winning-shot-alist
      '(("隆"   "波動拳" "昇龍拳" "竜巻旋風脚")
        ("拳"   "昇龍拳" "波動拳" "竜巻旋風脚")
        ("春麗" "スピニングバードキック" "百烈キック")
        ("E.本田" "スーパー頭付き" "百烈張り手")))

などとして alist を作っておきます。この alist から連想キーを 持っている list を取り出す関数が assoc で、

(assoc  連想キー  連想リスト)

とすることで、「連想リスト」の中から「連想キー」を持っているリスト一つを見 つけだしそのリストを返します。もし該当するキーを持つリストが見つからなかっ た場合は nil を返します。今回の winning-shot-alist に対して、 キーボードから読み込んだものをキーとして該当するものを探す場合は次のように します。

(let ((key (read-string "誰のわざ? ")) list)
  (setq list (assoc key winning-shot-alist))
  (if list
      (message "%sの必殺技は%sです。" key (cdr list))
    (message "%sって誰?" key)))

assoc で得られるリストは、連想キーを含めたリストですから、値だけを 取り出したい時は、その cdr 部を取る必要があります。

余談 7 マクロ

検索後に、グルーピングしておいた文字列を取得するために、 (buffer-substring (match-beginning 1) (match-end 1)) などとすること はほとんど定石と言ってよいでしょう。にもかかわらず、これを一気に行う関数が ありません(よね?)。そこで、(buffer-substring (match-beginning n) (match-end n)) という機能の関数を定義したいのですが、この程度で関数にする のはちょっと抵抗を感ずるかもしれません。このような場合に、C言語の引数付き define のような役割をする「マクロ」を利用すると良いでしょう。

Cのマクロ定義と違って Lisp のマクロの展開のされ方は少し特殊です。 次のCのマクロの例

#define inc(x)  ((x)++)

は、定義の引数以外は文字どおりに展開されます。しかし、Lisp のマクロ は事情が違います。(setq i (1+ i)) のような動作を行うマクロ定義は次 のようになります。

(defmacro inc(x)
  (list 'setq x (list '1+ x)))

このような定義を行った場合、Lisp プログラム中に (inc i) が出 現した場合 Lisp インタプリタはマクロ定義の仮引数 xi を代入した上でマクロ定義を Lisp そのものとして評価します。 つまり、上の定義のうち(クォートなしの) xi とみなし (list 'setq x (list '1+ x)) を評価します(実際に (setq x 'i) してからこの式を評価してみると良いでしょう)。そして、得られた結果が期待し たS式と同じになっているか確かめます。

余談練習小問題

    (buffer-substring (match-beginning 番号) (match-end 番号))

を表わすマクロを定義してみましょう。

completing-read

さて、これまで連想リストの説明をしてきたので、completing-read に 渡す補完テーブルの説明に移ります。completing-read の引数を復習して おきましょう。

(completing-read プロンプト 補完テーブル 選択(述語)関数
         要マッチ 初期入力)

completing-read の第二引数に補完テーブルを alist の形で渡し ます。例えば曜日を読み込む時の補完テーブルは次のような形です。

'(("Sun.") ("Mon.") ("Tue.") ("Wed.")
  ("Thu.") ("Fri.") ("Sat."))

今まで説明した alist とは少し形が違い、それぞれの(内側の)リストが連 想キーだけで構成されていて、それに対応する値が存在していません。補完入力を したいだけであれば、このような形で構いません。これを用いた曜日入力モジュー ルは次のようになります。

(defvar day-alist '(("Sun.") ("Mon.") ("Tue.") ("Wed.")
            ("Thu.") ("Fri.") ("Sat.")))
(defun read-day-of-the-week ()
  (interactive)
  (completing-read "Day of the week?: "
           day-alist nil t))

day-alist に連想キーとして入っている文字列を補完候補として入力を 促し、スペースやタブで補完しながら文字を読み込みます。この例では第四引数の 「要マッチ」が t なので候補以外の文字列は入力することができません。

さて、ここでは第三引数の「選択関数」のところには nil を指定しまし たが、ここには補完テーブル中の要素のうち、特定の条件を満たすもの(選 択関数の返す値が t)を候補にしたい場合に利用します。「選択関数」には alist の各要素が(リストのまま)渡されます。次の例では、月の名 前を読み込む時に、大の月だけを選んで補完候補とします。

(defvar month-alist
  '(("Jan." 31) ("Feb." 28) ("Mar." 31) ("Apr." 30)
    ("May." 31) ("Jun." 30) ("Jul." 31) ("Aug." 31)
    ("Sep." 30) ("Oct." 31) ("Nov." 30) ("Dec." 31)))
(defun read-odd-month ()
  (interactive)
  (completing-read "Odd month: " month-alist
           'check-odd-month t))
(defun check-odd-month (list)
  (eq 31 (car (cdr list))))

関数 check-odd-month には month-alist の各要素、すなわち '("Jan." 31), '("Feb." 28), ..., '("Dec." 31) が渡さ れるので、これらの第二要素を (car (cdr list)) によって取り出し、 `31' かどうかを判定した結果を返しています。

補完を制御する変数

変数 completion-ignore-caset にすると、補完文字列の大 文字小文字を無視します。例えば、月名を読む時に小文字で入力したイニシャルを 元に補完して欲しい時は、

(let ((completion-ignore-case t))
  :
  (completing-read ...)...)

のように completion-ignore-case に局所的に t をセットして補 完入力関数を呼びます。

try-completion

前出の completing-read はミニバッファで補完候補を読むものでしたが、 バッファ中にある文字列を元に補完したものを得たい場合などに用いるのが try-completion です。Emacs-lisp の関数を随時補完してくれる M-TAB がその代表的なものです。

(try-completion 文字列 補完テーブル [選択関数])

第一引数の「文字列」を「補完テーブル」中のすべての候補と比較し、マッチし たものがあった場合、マッチしたもの共通部分の先頭からの文字列を返します。も しマッチするものがなかった場合は nil を返し、「文字列」が「補完テー ブル」の中のただ一つの候補と完全一致した場合には、t を返します。少々 分かりにくいので例を挙げて説明します。

例えば補完候補に foo, bar, baz, bazz, hoge, hore があった場合、次のような補完結果が得られます。

「文字列(第一引数)」    try-completion の結果
        "f"                 "foo"
        "b"                 "ba"
        "ba"                "ba"
        "bar"               t
        "baz"               "baz"
        "h"                 "ho"
        "ho"                "ho"
        "hog"               "hoge"
        "x"                 nil

これらの結果を考察すると try-completion の返り値を次のように判断 すると良いことがわかります。

これより try-completion を用いてバッファ中の文字列の随時補完をす る手順は次のようになります。

(defun complete-something ()
  (interactive)
  (補完文字列の先頭を探す)                      ;;(*1)
  (先頭からポイントまでの文字列を取得)
  (setq 結果 (try-completion 文字列 テーブル))
  (cond
   ((eq 結果 t) (これ以上補完の必要ないと表示))
   ((eq 結果 nil) (一致するものがないと表示))
   ((string= 文字列 結果)
    (先頭からポイントまでを切り取り、結果で置き換える))
   (t (候補一覧を表示))))                       ;;(*2)

(*1)は一般的に、文字列の要素となり得ないものを後方に探します。例えば改行 文字や、空白文字は補完文字列とならないことがほとんどなので、 (skip-chars-backward "^ \t\n") とすれば十分でしょう。

続いて(*2)のための関数を紹介します。

all-completions display-completion-list

関数 all-completionstry-completion と同じ引数を取り(第 二引数まで)、一致する文字列候補の全てをリストにして返します。例えば先の例 で、文字列 `ba' で補完させる場合に一致する候補は、`bar', `baz', `bazz' ですから、これらをリストにした、'("bar" "baz" "bazz")all-completions の結果となります。これをバッファ に表示したい時に利用するのが display-completion-list で、 all-completions で得られた結果をカレントバッファに表示します。

実際にはカレントバッファに候補一覧を表示しては困るので、隣に新たなバッファ を開いて表示します。この方法に関しては、ウィンドウとバッファの取り扱いを覚 えてから説明します。

Concept Index

Jump to: a - c - d - e - f - i - k - l - m - n - o - p - r - s - t - v - - - - - - - - - - - - - - - - - - - - - - - - - - - -

a

  • absolute motion
  • arithmetic
  • atom
  • c

  • characters, skipping
  • completion, controlling variables
  • completion, input
  • control structure
  • cursor motion
  • d

  • debugging
  • declaring, local maps
  • defining, functions
  • defining, major mode functions
  • e

  • Emacs-Lisp, construction
  • Emacs-Lisp, scope
  • escape characters
  • evaluating programs
  • Exercise 1
  • Exercise 2
  • Exercise 3-1
  • Exercise 3-2
  • Exercise 4-1
  • f

  • Functions and Variables
  • functions, defining
  • functions, finding
  • functions, must know
  • i

  • Input Functions, Input Functions
  • interactive functions
  • interactive functions, binding to keys
  • k

  • keymaps, setting
  • keys, binding to interactive functions
  • keys, setting
  • l

  • list
  • list related predicates
  • list, accessing elements
  • list, adding elements
  • list, appending
  • list, association
  • list, creating
  • list, structure
  • local maps, declaring
  • m

  • macros
  • major mode, defining functions
  • major mode, requirements
  • major mode, what
  • major mode, writing
  • Metachars
  • mode name, setting
  • motion, absolute
  • motion, boundary check
  • motion, by char
  • motion, by line
  • motion, by search
  • motion, by word
  • motion, cursor
  • motion, point
  • motion, relative
  • motion, to a column
  • motion, to a line
  • n

  • numerical transformation
  • o

  • operators
  • p

  • point, saving position
  • r

  • relative motion
  • replacement, strings
  • restricting variables
  • s

  • scope, of Emacs-lisp
  • search and move
  • search, accessing results
  • search, regexp
  • search, strings
  • search, word
  • setting, keymaps
  • setting, keys
  • setting, mode names
  • skipping characters
  • strings, deleting
  • strings, getting
  • strings, insert
  • strings, insert/delete
  • strings, manipulation functions
  • strings, replacement
  • strings, transformation
  • strings, transforming to other types
  • symbol
  • t

  • transformation, numerical
  • transformation, strings
  • transformation, type
  • transforming, strings to other types
  • try-completion
  • type transformation
  • v

  • Variables
  • Variables, declaring
  • アトム[あとむ]
  • 移動 検索[いとうけんさく]
  • 移動 行[いとうきよう]
  • 移動 word 単位[いとうわあとたんい]
  • インタラクティブ関数 キーにバインド[いんたらくてふかんすうきい]
  • 移動 相対[いとうそうたい]
  • 移動 char 単位[いとうちやあたんい]
  • 移動 絶対[いとうせつたい]
  • 移動 桁[いとうけた]
  • 移動 境界の検査[いとうきようかいのけんさ]
  • 移動 ポイント[いとうほいんと]
  • インタラクティブ関数[いんたらくていふかんすう]
  • Emacs-Lisp スコープ[いいまくすりすふすこうふ]
  • 移動 line[いとうらいん]
  • 移動 カーソル[いとうかあそる]
  • Emacs-Lisp の構文[いいまくすりすふのこうふん]
  • エスケープキャラクタ[えすけえふきやらくた]
  • 演算子[えんさんし]
  • 関数 補完入力[かんすうほかんにゆうりよく]
  • 関数 文字列操作[かんすうもしれつそうさ]
  • 関数 雑[かんすうさつ]
  • 関数 最低限[かんすうさいていけん]
  • カーソル移動[かあそるいとう]
  • 型変換[かたへんかん]
  • 関数・変数[かんすうとへんすう]
  • 関数 算術[かんすうさんしゆつ]
  • 関数 定義する[かんすうていきする]
  • 関数 リスト処理[かんすうりすとしより]
  • 関数 一般入力[かんすういつはんにゆうりよく]
  • 関数 探す[かんすうさかす]
  • 関数 バッファ編集[かんすうはつふあへんしゆう]
  • キー 割当て[きいわりあて]
  • 行移動[きよういとう]
  • キーマップ 設定[きいまつふせつてい]
  • 検索 関数[けんさくけつか]
  • 検索 結果へのアクセス[けんさくけつかへのあくせす]
  • 検索, 文字列[けんさくもしれつ]
  • 検索 単語[けんさくたんこ]
  • 検索 正規表現[けんさくせいきひょうけん]
  • 桁移動[けたいとう]
  • 検索移動[けんさくいとう]
  • コンスセル[こんすせる]
  • コンセル[こんせる]
  • 算術[さんしゆつ]
  • 算術関数[さんしゆつかんすう]
  • 削除 文字列[さくしょもしれつ]
  • 述語関数[しゆつこかんすう]
  • 章末問題 1[しようまつもんたい1]
  • シンボル[しんほる]
  • 章末問題 2[しようまつもんたい2]
  • 数値変換[すうちへんかん]
  • 制御構造[せいきよこうそう]
  • 絶対移動[せつたいいとう], 絶対移動[せつたいいとう]
  • 正規表現検索[せいきひようけんけんさく]
  • 相対移動[そうたいいとう]
  • 挿入 文字列[そうにゆうもしれつ]
  • 単語検索[たんこけんさく]
  • 置換 文字列[ちかんもしれつ]
  • char 単位移動[ちやあたんいいとう]
  • デバッギング[てはつきんく]
  • バッファ編集関数[はつふあへんしゆうかんすう]
  • プログラムの評価[ふろくらむのひようか]
  • 変換 型[へんかんかた]
  • 変換 数値[へんかんすうち]
  • 変換 文字列と他の型[へんかんもしれつとほかのかた]
  • 変数 宣言[へんすうせんけん]
  • 変数[へんすう]
  • 変換 文字列[へんかんもしれつ]
  • 変数 束縛[へんすうそくはく]
  • 変数 補完を制御する[へんすうほかんをせいきよする]
  • ポインタ[ほいんた], ポインタ[ほいんた]
  • ポイント[ほいんと]
  • 補完 制御する変数[ほかんせいきよするへんすう]
  • ポイント 位置の保存[ほいんといちのほそん]
  • ポイント移動[ほいんといとう]
  • 補完入力[ほかんにゆうりよく]
  • まとめ 1[まとめ1]
  • マクロ[まくろ]
  • メタキャラクタ[めたきやらくた]
  • メジャーモード を書こう[めしやもおとをかこう]
  • メジャーモード 必須条件[めしやもおとひすうしおうけん]
  • メジャーモード ってなに[めしやもおとつてなに]
  • メジャーモード関数 定義[めしやもおとかんすうていき]
  • 文字列 他の型の変換[もしれつほかのかたのへんかん]
  • 文字列 取り込み[もしれつとりこみ]
  • 文字列 挿入/削除[もしれつそうにゆうさくしよ]
  • 文字(種)スキップ[もしすきふ]
  • 文字列 置換[もしれつちかん]
  • 文字列 検索[もしれつけんさく]
  • 文字列 挿入[もしれつそうにゅう]
  • 文字列 変換[もしれつへんかん]
  • モード名 設定[もおとめいせつてい]
  • 文字列 削除[もしれつさくしよ]
  • 余談 2 関数・変数[よたん2]
  • 余談 3 デバッギング[よたん3]
  • 余談 6 Emacs-Lisp のスコープ[よたん6]
  • 余談 1 「〜関係の関数はないかな?」を探す[よたん1]
  • 余談 7 マクロ[よたん7]
  • 余談 4 プログラムの評価[よたん4]
  • 余談 5 雑関数[よたん5]
  • line 移動[らいんいとう]
  • リスト[りすと]
  • リスト 作成[りすとさくせい]
  • リスト 要素の追加[りすとようそのついか]
  • リスト 要素の参照[りすとようそのさんしよう]
  • リスト 構造[りすとこうそう]
  • リスト 連想[りすとれんそう]
  • リスト 結合[りすとけつこう]
  • リスト 処理関数[りすとしよりかんすう]
  • 練習問題 4-1[れんしゆうもんたい41]
  • 練習問題 3-2[れんしゆうもんたい32]
  • 練習問題 3-1[れんしゆうもんたい31]
  • ローカルマップ 使用宣言[ろおかるまふしようせんけん]
  • word 単位移動[わあとたんいいとう]
  • Function Index

    Jump to: $ - % - ' - ( - * - + - - - . - 1 - < - ? - \ - ^ - a - b - c - d - e - f - g - i - k - l - m - n - o - p - r - s - t - u - w - x - -

    $

  • $
  • %

  • %, *, +, -, /
  • '

  • '
  • (

  • (bobp) / (eobp)
  • (bolp) / (eolp)
  • *

  • *
  • +

  • +
  • -

  • [-^A-Z]
  • .

  • .
  • 1

  • 1+, 1-
  • <

  • &#60;, &#60;=, =, /=, &#62;, &#62;=
  • ?

  • ?
  • \

  • \"
  • \&#38;
  • \\, \\
  • \a
  • \b
  • \C-英字
  • \e
  • \f
  • \n, \n
  • \r
  • \t
  • ^

  • ^
  • [^文字リスト]
  • a

  • alist
  • all-completion
  • and
  • append, append
  • apropos
  • Arithmetic Functions
  • assoc
  • atom
  • b

  • backward-char
  • backward-word
  • beginning-of-buffer
  • beginning-of-line
  • Buffer Editing Functions
  • buffer-substring
  • c

  • car, car
  • catch
  • cdr, cdr
  • completing-read, completing-read
  • concat
  • cond
  • cons, cons
  • count-lines
  • d

  • Declaring Variables
  • define-key
  • Defining Functions
  • defmacro
  • defun
  • defvar
  • delete-backward-char
  • delete-char
  • delete-region
  • ding
  • display-completion-list
  • e

  • end-of-buffer
  • end-of-line
  • erase-buffer
  • f

  • Finding Functions
  • format
  • forward-char
  • forward-line
  • forward-word
  • g

  • goto-char
  • goto-line
  • i

  • if
  • input, completion
  • input, normal
  • insert
  • insert-char
  • int-to-string
  • interactive
  • Interactive Functions
  • k

  • kill-region
  • l

  • length
  • let
  • let*
  • list, list
  • List Manipulation
  • list related predicates
  • listp
  • m

  • make-keymap, make-keymap
  • match-beginning
  • match-end
  • max, min
  • message
  • Misc Functions
  • move-to-column
  • n

  • next-line
  • nth
  • numberp(integerp)
  • o

  • or
  • p

  • point
  • point-max
  • point-min
  • predicates, list related
  • previous-line
  • progn
  • r

  • random
  • re-search-backward
  • re-search-forward
  • read-file-name
  • read-string
  • replace-match
  • Restricting Variables
  • s

  • save-excursion
  • Search Functions
  • search-backward
  • search-forward
  • self-insert-command
  • set
  • setq
  • sit-for, sit-for
  • skip-chars-backward
  • skip-chars-forward
  • sleep-for
  • string-equal
  • string-match
  • string-to-char
  • string-to-int
  • string<
  • string=
  • string>
  • stringp
  • Strings manipulation
  • substring
  • t

  • this-command-keys
  • throw
  • try-completion
  • u

  • use-local-map
  • w

  • while
  • word-search-backward
  • word-search-forward
  • x

  • [X-Y]
  • 文字列操作[もしれつそうさ]
  • [文字リスト]
  • Variable Index

    Jump to: a - c - g - k - m

    a

  • alist
  • auto-mode-alist
  • c

  • case-fold-search
  • Completion Controlling Variables
  • completion-ignore-case
  • current-column
  • g

  • global-map
  • k

  • kill-ring
  • m

  • mode-name
  • Jump to: c - e - m

    c

  • C-j
  • e

  • ESC C-x
  • ESC ESC
  • ESC TAB
  • m

  • M-TAB

  • This document was generated on 29 January 1998 using the texi2html translator version 1.52.