ということで、今日から隔週の水曜日はEmacs Lispで遊ぶことにする。
基本的に以下の内容は、青柳龍也さん著『UNIX短編シリーズ Emacs Lisp』(クォリティ刊)の内容に沿っている。
より正しい説明をちゃんとした文書で読むにはそちらを当たってください。
まず、Lisp Interactionモードのファイルを作る。
これは、ファイルの先頭に
; -*- lisp-interaction -*-
と書けば良い。
セミコロン(;)から改行まではEmacs Lispではコメントになるが、ファイルの先頭にいきなりこの行を書き、それをEmacsで開くと、lisp-interactionモードになる。

Perlのスクリプトの先頭に
#!/usr/bin/perl
とシュバング行を書くのに似ている。

Emacsを起動すると作られる*scratch*バッファーもlisp-interactionモードである。
消してもC-x C-f *scratch*と入力すれば新規作成出来る。
名前が*で始まるバッファーはEmacsを終了するとき保存するかどうか聞かれず、そのまま終了すると消えてしまう。

式の計算

次の行の行末(")"の後ろ)にカーソルをおいて、C-j(Ctrl+JのことをEmacsではこう書く)を押す。
(+ 1 2)
3
1+2の答えが出る。

これは+関数に2つの引数1と2を渡したものである。
次の行だとエラーになる。
(+1 2)
別ウィンドウが開いて、
Debugger entered--Lisp error: (invalid-function 1)
(1 2)
と表示される。
+1という関数がないから怒られている。
+1はプラスイチとして解釈され、1という関数がありませんよと怒られているのだ。

このように、複数の要素をスペースで区切ってカッコで括ったものをリストと言う。
リストの最初の要素に関数を書くと、残り全部が引数として渡された戻り値という値を持つ。
(関数 要素1 要素2 要素3...)
引数を増やしてみる。
(+ 1 2 3 4 5 6 7 8 9 10)
55
掛け算にしてみる。
(* 2 2 2)
8
引き算も使える。小数も使える。
(- 10 3.5)
6.5
割り算も出来る。
引数がすべて整数だと、整数で計算する。
小数点以下切り捨てになる。
(/ 10 4)
2
引数の一方または両方が小数だと小数で計算する。
(/ 10 3.0)
3.3333333333333335
引数がまた式でも良い。
以下は25、22、20の平均を求めている。
(/ (+ 25 22 20) 3.0)
22.333333333333332
これはノースリーブスの平均年齢だ。
合計年齢を求めるリストを第一要素とする、3で割るリストを作っている。

関数プログラミング

以下の式をC-jで評価する。
(defun circle (r) ; 半径から円の面積を求める
(* r r 3.14))
circleと関数名が返る。
と同時にcircle関数がロードされる。
次の式を評価する。
(circle 10)
314.0という数値が返る。
3.14という小数が関数の中に入っているから、答えが小数になっている。

一般に、
(defun 関数名 (引数) 式)
と書くと関数名の関数が評価され、引数を得て、式の値を返す。
(defunはdefine functionのことか)

引数を2つ渡してみる
(defun sankaku (teihen takasa) ; 底辺と高さから三角形の面積を求める
(/ (* teihen takasa) 2))
(sankaku 2 3)
3

関数から関数を呼び出せる。
(defun cylinder (r h) ; 半径と高さから円柱の体積を求める
(* (circle r) h))
(cylinder 10 10)
3140.0

日本語プログラミング

変数、関数名は日本語でもいい。
(defun 円の面積 (半径) ; 半径から円の面積を求める
(* 半径 半径 3.14))
円の面積

(defun 円柱の体積 (半径 高さ) ; 半径と高さから円柱の体積を求める
(* (円の面積 半径) 高さ))
円柱の体積

(円柱の体積 10 100)
31400.0
全角のカッコ、空白はさすがにダメ。
(円柱の体積 10 10)
()と空白は半角にすること。

関数は別ファイルに切り分けられる

;;; circle.el --- 円関係の関数集

(defun circle (r) ; 半径から円の面積を求める
(* r r 3.14))

(defun cylinder (r h) ; 半径と高さから円柱の体積を求める
(* (circle r) h))
をcircle.elに書いておいて、M-x load-fileと入力する。

circle_el


M-はメタキーを押しながらという意味で、昔のコンピューターにはメタキーというのがついていた。
今はEscを押して、放すとメタキーを押したことになる。
あと、Altキーを押しながらでも同様の操作ができる。

M-x load-fileで.elファイルがロード出来る。
ロードするファイルの場所を聞かれるので、circle.elを指定する。
.elはEmacs Lispにありがちな拡張子で、このファイルを開くと、自動的にEmacs-Lispメジャーモードになる。

関数をロード/実行する他のやり方

ここまでで関数をロードするやり方としてはこれがあった。

* (defun)リストの末尾でC-j
  =>その次の行に関数名が挿入され、関数がロードされる

* (defun)リストをファイル「〜.el」にまとめ、「M-x load-file 〜.el」を実行する

他に、以下のやり方がある。

* (defun)リストの末尾でC-x C-e
  =>ミニバッファに関数名が表示され、関数がロードされる

C-x C-e(Ctrl+Xを押下してからCtrl+E)は、C-jと同じく直前のリストを評価する。
ただ結果は、ミニバッファに表示する。
行が追加されないのでこの方がプログラミング中は便利かも。

* (defun)リスト群を含んでいるバッファー(編集中のファイル)でM-x eval-buffer

現在編集中のファイルにM-x load-fileしたのと同じことになる。

* (defun)リスト群を含む行をマウスで選択するか、C-SPCで先頭にマークを打ってからカーソルを動かしてリージョン(選択範囲)に入れ、M-x eval-region

リージョンだけが評価(evaluate)される。
(円柱の体積 10 100)
のような、defun以外の文もC-x C-eで実行出来る。
(結果はミニバッファに出てくる)
こちらはM-x eval-regionで評価しても、結果が表示されない。

関数のヘルプ

上記で、円の面積を
(defun circle (r) ; 半径から円の面積を求める
(* r r 3.14))
と求めているが、円周率が小数のために結果も小数になる。これを四捨五入するにはどうすればいいだろうか。

EmacsのオンラインヘルプInfoを調べる。
M-x infoでヘルプが起動する。
mキーを押下するとmenu item:というプロンプトがミニバッファに出現する。
ここでelispと書いてリターンを押すと、Emacs Lispのマニュアルに遷移する。

elisp_info

Numbersというリンクをクリックすると数値関数のマニュアルに遷移する。
Rounding Operationというリンクをクリックすると丸め操作のマニュアルに遷移する。
fround(floatのround、浮動小数点の丸め)という関数が求めるもののようだ。
(fround 3.4)
3.0
(fround 3.5)
4.0
なるほどうまく行っている。
では、これを使って結果を丸める円の面積、round-circleを作る。
(defun round-circle (r) ; 半径から円の面積を求めるが、結果は丸める
(round (circle r)))
(circle 1)
3.14
(round-circle 1)
3
(circle 2)
12.56
(round-circle 2)
13
なるほどー。

Infoの見方。
 * M-x Infoで起動
 * mElispでEmacs Lispに移動
 * カーソルを上下左右に動かしたり、C-sでサーチしたりして目的の項目に到達
 * lで前の画面に戻る(なんの略だろ。last?)
 * qでInfoを抜けて前のバッファに戻る(quitだろう)
 * ?でInfoの使い方が分かる

froなんとかとかいうんじゃなかったかなー、と思っている時はM-x describe-functionでfroまでキーを押してTABキーを押すと関数名が保管され、RETで説明が表示される。
説明は説明のバッファでqで脱出する。

関数名、たとえばdefunの上にカーソルを置いてM-x describe-functionすると、Describe Function (default: defun)と表示される。
この状態でRETを押下すると、説明が表示される。

関数の引数は大文字になっている。

describe-variableで引数のヘルプが出る。

補完

ミニバッファで関数名を書くときはTABで補完できるが、Lisp Interactionモードのバッファの中でも補完できる。
キーはESC TABである。
froと書いてESC TABを押下するとfroundと表示される。
(ただしこのような文章を書いていて直前に日本語文字がくると、くっついてしまって補完が出来ない。これが目下の問題)
なお、ユーザーの作成した関数や変数、たとえばcircleも補完できる。