Elisp (Emacs Lisp) 入門--Perl や C プログラマー用

最終更新日: (創設: 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 に渡される。

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

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

  5. buffer との入出力

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

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

    	(defun some-function (beg end) (interactive "r") ...)
        
    "r" により、この関数を起動したバッファの 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 と現在のカーソル位置で挟まれた領域) の文中に発見すると、項目ごとの金額と使った現金の合計が得られる。 詳細はプログラムのコメント欄を参照。

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