Perl や C プログラマーのための Elisp (Emacs Lisp) 入門

最終更新日: (創設: 2005-01-24)


詳細は、info に emacs lisp manual をインストールし、それを 見るのがよいが、これは、それを見るためのガイドである。manual を最初から 読むのが大変な場合には、manual の最初のページにある Index で、以下の 説明に現われる用語を検索してみるのがよいであろう。

プログラムに当り、改行は好きに入れてよい。emacs の lisp-mode で 編集すると、各行で TAB を押すと、最適 indent をしてくれる。また、 region 内すべてに対し、M-x indent-region で同じことをしてくれる。

  1. 関数の定義

    標準は

    	(defun 関数名 (引数リスト) "コメント" 関数の実体)
       
    であるが、これは、プログラム中で引用する関数。M-x ... で 読み出せるようにするには、
    	(defun 関数名 (引数リスト)
    		(interactive ...) "コメント" 関数の実体)
       
    としなければならない。interactive ... の ... の書き方については以後を 参照。引数リストは interactive の ... の書き方に合わせる。実用的には、 どんなプログラミングも、通常、この関数記述からスタートする。C の main() みたいなもの。

  2. 手続き型記述

    基本的には perl と似ている。lisp というと、( ) 内で評価された結果を 使ってさらにその外側の ( ) を評価して、... となるのが基本であるが、 実際には手続き型に書かれることが多いので、正当な lisp 記述は余り 気にしなくてよい。

    一般に、defun の中の関数の実体は、次のような形で書かれることが多い。

    	(let (
    	      (内部変数)
    	      (内部変数)
    	      (内部変数 式)
    	      (内部変数 式)
    	      ...
    	      )
    	     実行文
    	     実行文
    	     ...
                 )
       
    最初の部分が、内部で使う変数の宣言である。必要に応じ、初期化もできる。 続いて、実行文が並ぶが、これらは逐次、上から処理される。この関数全体の 戻値は、最後の実行文の戻値で決まる。

    各実行文の中で、やはり逐次処理が必要となる場合には

    	(progn (
    		実行文
    		実行文
    		...
    		)
       
    と記述することができる。

  3. 制御文
    1. if 文: (if 条件 true文 false文 false文 ...)

      false文はなくてもよい。複数あると順に実行していく。if 文にも 戻値がある。実行された方の文の戻値。

    2. while 文: (while 条件 実行文)

      戻値は while 文を抜け出るときの条件で、ほとんどの場合 nil である。 異常脱出は throw, catch で行う。

      	(catch 'tag (while 条件 (...) (throw 'tag t)) 以後の処理)
          
    3. cond 文: (cond (条件 実行文) (条件 実行文) ...)

      if then elsif elsif に対応するような lisp 文。最初から条件を 見ていき、最初に条件が成立したところの実行文を実行し、その戻値を戻す。 最後を default 文にしたい時には (t 実行文) を置く。

  4. mini-buffer との入出力

    mini-buffer (emacs の一番下に現われる細い buffer) から入力を 行うには defun の定義の (interactive ...) で

    	(defun some-function (str) (interactive "s問い合せ文: ") ...)
       
    とする。interactive の最初の s は mini-buffer から読みこむデータが 文字列であることを指す。また、読み込まれた文字列は、関数の引数 str に 渡される。

    その他、数字を読み込んだり、バッファ名を読み込んだりできる。(manual 参照)

    出力は (message 文字列) で可能。

  5. buffer との入出力

    もっとも使い道の多いのが、emacs のバッファ (emacs の windows に 対応するが、隠れているものもバッファである) からの読み込みと、そこへの 書き出しであろう。

    読み込みも region (任意の場所で C-w を押すとそこが始点となり、 カーソルを動かした先が終点となる) を指定して、その間の領域の文章を 処理することが多いだろう。その場合には次のようにする。

    	(defun some-function (beg end) (interactive "p") ...)
       
    "p" により、この関数を起動したバッファの region の始点終点を手に 入れることを宣言する。手に入れた始点と終点は関数引数の beg と end に 渡される。

    バッファに文字列を書き出すには、

    	(insert-before-markers str)
       
    とすれば、現在カーソルのある点より書き出される。一般には、異なる バッファに書き出すことが多いであろうが、その場合には、扱っている バッファを切り替える必要がある。
    	(save-current-buffer
    	  (set-buffer (get-buffer-create "*another-buffer*"))
    	  (insert-before-markers str))
       
    another-buffer があれば、そこへ書き出し、なければ新しく作成して、 そこへ書き出す。最後に書き出したバッファを表示したければ
    	(switch-to-buffer-other-window "*another-buffer*")
       
    とする。

  6. debug

    デバッグができないと、ちょっと大きなプログラムは完成しない。lisp の デバッガには色々あり、詳細は manual を見て欲しいが、私がよく使うのは、 怪しそうな所に (debug) なる命令を嵌めこむ方法である。こうすると、 そこでプログラムは停止してくれる。しかもデバッガーのバッファで e (eval) とすると、その際の色々な変数の状態を見ることができる。 これにより、変数が期待通りになっているか確認することができる。

    続けて実行するには c (continue)、一行単位の実行ならば d (debug)、 終了は q である。デバッグが終ったら必ず q すること。

    通常、デバッグ時には、プログラムの画面を出しながら行うことが多い。 (interactive ...) が設定されていると結構面倒である。そこで、私は (interactive ...) をコメントアウトし、(some-function 実引数 ...) といった形で、引数を確定してこの関数を呼び出す。文章処理などで、region 指定する場合には、プログラムと同じ画面の最初に、文章を入れ、実引数に 始点と終点の数字を与えて、この関数を呼び出す。

    慣れてくると、完成プログラムに対し、M-x eval-buffer を実行し、 M-x set-variable で debug-on-error を t にして、Error の発生した ところで、e を押して、各変数などをチェックする、もしくはプログラム中に (debug) を挿入して、そこでチェックするのが有効である。

    もっとよい方法があるかも知れないが、今のところ、これで満足している。

  7. 文字列処理と正規表現

    正規表現で TAB や CR は \t、\n とするが、グループ化の括弧は \\(、 \\) などとする。例えば、beg から end までの文字列で、最初の区切り 記号以外を word へ、次の数字列を number に入れるには、次のようにする。

    	(goto-char beg)
    	(re-search-forward "\\([^ \n\t]+\\)\\[ \t]+\\(-?[0-9]+\\)" end t)
    	(setq word (match-string-no-properties 1))
    	(setq number (match-string-no-properties 2))
       

    Perl では文字列処理が行単位で行われるが、Emacs Lisp では対象全体、 つまりバッファを指定すればバッファ全体、region を指定すれば region 全体に対して行われる。つまり、全体をあたかも一行のように感じて、作業が 行われる。これを勘違いすると、大きな間違いになる。どうしても行単位に 処理したいときには、面倒であるが、次のようにする。

    	(end-of-line)
    	(setq eol (point))	; 行末の位置を覚える
    	(beginning-of-line)	; 行頭へ移動
    	(re-search-forward "regexp" eol t) ; 例えばこうした検索を行う
    	...
    	(forward-line 1)	; 次の行へ行く
       

  8. 連想記憶

    Perl で、色々なものの整理には連想記憶が圧倒的力を持っているが、 Emacs Lisp ではオブジェクト配列がほぼこれに対応する。配列といっても、 リストが底辺にあるので、リスト名を決め、その key と value をいじるという形になる。例えば配列名を dic としよう。配列の作成は

    	(setq dic (make-vector 3 0))
       
    とする。リストに key を与えて、value を読み出すには
    	(symbol-value (intern-soft key dic))
       
    とする。また、書き込みは
    	(set (intern key dic) (list value))
       
    とする。Perl でよく行う集計 (ここでは総和の計算) では、最初に key が 登録されていないときには、作業が異なるので、次のようにする。
    	(setq total
    	  (+ (car (or (symbol-value (intern-soft key dic))
    	  			  (set (intern key dic) '(0 nil)))) amount))
       

  9. 実例

    小遣い帳、現金出納帳、複式簿記といった金銭の記録を emacs で 付けて (特に howm などで) いる人のために、emacs 上で動く簡単な 金銭集計プログラム accounting.el を実例として示す。 上に述べた手法がほとんど、使われている。

    	タグ 科目 金額 日付 備考
    	タグ 左借方科目 右貸方科目 金額 日付 備考
       
    の形の文字列を選んだ region 内 (C-space と現在のカーソル位置で挟まれた 領域) の文中に発見すると、項目ごとの金額と使った現金の合計が得られる。 詳細はプログラムのコメント欄を参照。

"岡部の公開文書のページ"へ