Emacs lisp 実践的基礎メモ書き

公開日時:2022-03-03 Thu. 21:01:54 JST
by kiyozzy

■ 前提

今自分が調べて理解しているEmacsの設定ファイルを書く時に必要となる知識を、
時が経って忘れてしまった時に、たくさんの文章を再び読まずに済むよう、自分のためのメモ。
用語や概念などを正確に書くというより、実践的観点で書き、間違いは許容する。

Emacsの設定ファイル自体は何度も手を加えた経験があり、
(setq myvalue 365)
とあれば、変数 myvalue に値 365 を設定するのだな、とか、
(message "hello")
とあれば、"hello" とメッセージを表示するのだな、とかは理解している。
Web検索で "emacs lisp 分岐" などを検索して、分岐処理を書いたりは経験がある。
もっとガッツリ設定を書いているのだが、時間が空いて見たら、理解できなかった。

■ リストの評価はリストの最初の項目を関数として扱いその関数を実行する

重要な事をこの時点で書いておく。
丸括弧で囲まれている部分は lisp の基本であるリストであるが、先の例の様にリストを書くと評価が行われ、
リストが評価される場合は、最初の項目は関数シンボルとして扱われ、残りはその関数の引数となる。
(display-graphic-p frame)
とあれば、関数 display-graphic-p を 引数 frame(を評価した後) で呼び出すということである。
数値や文字列、シンボルなどアトミックな型や、リストを評価しないで欲しい場合はクォート"'"をつける。
'(display-graphic-p frame)
とすれば、評価は行われず、display-graphic-pというシンボルとframeというシンボルのリスト
(display-graphic-p frame)
となる。
(1 2 3)と書くと、"1"という関数がないというエラーとなるが、'(1 2 3)と書くとリスト(1 2 3)が得られる。

■ コンスセルとリスト

リストは、プリミティブな型ではなく、コンスセルという基礎的は型の連結から成り立っている。
コンスセルは、CAR(カー)とCDR(クダー)の2つのセルが結びついたものである。
CARとCDRの意味は今の姿を表していず、古い機械語命令から由来していて、
"Contents of the Address part of the Register", "Contents of the Decrement part of the Register" である。
コンスセルを作るには、cons(constructの意)を使うか、ドットで区切ってクォートした丸括弧で囲むと作れる。
(cons 1 2)
'(1 . 2)
コンスセルのCARのセルはcar関数で取り出せ、CDRのセルはcdr関数で取り出せる。
(car (cons 1 2)) は 1
(cdr (cons 1 2)) は 2

リストを記載するには、丸括弧で囲み、項目をスペースで区切る。
'(1 2 3)
が、例えばリストである。

リストは、コンスセルのCDRのセルにコンスセルが入っていて(指していて)、
最後のコンスセルのCDRがnilになっているものである。
リスト
'(1 2 3)

(cons 1 (cons 2 (cons 3 nil)))
である。

(car '(1 2 3))
とすると 1 が得られ、
(cdr '(1 2 3))
とすると、(2 3)が得られる。

リストの指定番目のCARを取り出す場合は nth を使う。
(nth 1 '(1 2 3)) は 2

■ クォートと評価

先程から数値を例にしているので、(cons 1 2)と'(1 . 2)の違いがわからないが、クォートを使うと評価しないので、
変数を評価して使いたい場合に違いが出る。
例えば
(setq myvalue 365)
として、 myvalue を評価した 365 を使いたい場合である。
(cons 1 myvalue)
とすると変数シンボルは評価されて、(1 . 365) となるが、
'(1 . myvalue)
とすると変数シンボルは評価されず、(1 . myvalue) となる。

リストの場合
'(1 2 myvalue)
とすると変数シンボルは評価されず、(1 2 myvalue) となる。
(1 2 myvalue)
とすると、最初に述べたように、最初の項目"1"が関数として扱われ、エラーとなる。

Emacsの設定ファイルで関数などを書いていて、途中の計算結果などを変数に収めたものを、
呼び出す関数の引数で使いたい場合などは、評価しないシンボルとしてクォートしたコンスセルやリストを使うのではなく、
変数を評価したコンスセルやリストを使いたいはずだ。

変数シンボルを評価してコンスセルを作りたい場合は関数consを使えばよく、
変数シンボルを評価してリストを作りたい場合は関数listを使えば良い。
(list 1 2 myvalue)
とすると変数シンボルは評価されて、(1 2 365) のリストとなる。
引数でアトムでなく評価されたリストが要求されている場合などはlist関数を使えば良い。
(set-frame-font my-font-simple-full nil (list frame))
など。

CARに評価しないシンボル(ラベル)、CDRに変数を評価した値を入れたい場合は、次のように書けば良い。
(cons 'font my-font-simple-full)

具体例を書くと、フォントの設定をWeb検索して次の文が見つかったとする。
(add-to-list 'default-frame-alist '(font . "Migu 1M:pixelsize=12:spacing=0"))
このコンスセルの場合、fontは変数ではなくラベル名としてシンボルを使っていて評価はしない、
"Migu 1M:pixelsize=12:spacing=0"は即値なので評価は不要、なのでクォートしたドット記法のコンスセルが使われている。
で、"Migu 1M:pixelsize=12:spacing=0"を変数で指定したい場合は、次のように書けば良い。
(setq myfont "Migu 1M:pixelsize=12:spacing=0")
(add-to-list 'default-frame-alist (cons 'font myfont))

評価しなったリストやシンボルを評価するにはeval関数を使う。
(setq mycal '(+ 1 2))
としていて、
'mycal
は mycal となり、
mycal
は (+ 1 2) となるが、evalで評価して、
(eval mycal)
とすると 3 となる。
(eval 'mycal)
は (+ 1 2) である。

別の例も挙げておく。
(setq myvalue 365)
'(1 2 myvalue) は (1 2 myvalue)
(eval (cons 'list '(1 2 myvalue))) は (1 2 365)

■ シンボル

lispは、数や文字列など、アトムという分解できない型を持つが、変数や関数のシンボル(シンボル名)もアトムである。
C言語系の知識では理解しにくいが、シンボルは識別子として数値や文字列と同様に扱える。一般的なラベル名と言うと分かりやすいか。
シンボルを、ラベル名のような識別子として扱う場合や、評価して中身を取り出してはいけない場合などの時は、
クォートしたシンボルを使う。
(add-hook 'after-make-frame-functions 'my-set-font)
(color-values (face-background 'region frame))
などである。