Emacs Lispまとめ
Emacsで使われているLisp方言. ELISP(elisp)と省略されることも多い.
- 末尾再帰の最適化をしない
- 動的スコープ
- 関数型言語ではない.
Emacs Lisp: 文法
変数と定数
Emacs Lispは 動的スコープ を採用しておりどこでも値を参照と変更ができる.
setq で変数を宣言する. defconst というシンタックスもあるがこれは可読性だけのもので実際は変更できるとか.
ref. Constant Variables (GNU Emacs Lisp Reference Manual)
List操作
list
listを構築する.
-
アポストロフィ(‘)とlistでの挙動の違い
アポストロフィでリストをつくると中の変数は評価されないが, listをつかうと評価される.
(setq a "1") (setq b "2") (setq ab '(a b)) ;=> (a b) (setq ab2 (list a b)) ;=> ("1" "2")
append
リストを結合.
(setq test-list
(append test-list '("baz")))
;=> ("foo" "bar" "baz")2つのリストを結合した新しいリストを返すのでそれを変数にbindする.
add-to-list
リストの先頭に要素を追加. すでに要素があるときは置き換える.
そのためappendよりもデバッグがしやすいので設定はこっちがいい.
一方ひとつの要素しかリストに追加できない.
インクリメント
1+ 1-という記法がある. また incf, decf という関数もある.
(setq test-value 0)
(setq test-value (+ test-value 1))
(setq test-value (1+ test-value))
(incf test-value)
(setq test-value (1- test-value))
(decf test-value)ガード処理
条件をみたさない場合は, 処理を途中で中断したい. return を 返すのは発想が手続き的.
if, when を利用する.
遅延評価
起動時に必ず読み込む必要ない関数なら autoload を使う - すぎゃーんメモ
これだと, Emacs 起動時に require される.
(require 'js2-mode)
(add-to-list 'auto-mode-alist '("\\.js$" . js2-mode))これだと, .js を開いたときに require される.
(autoload 'js2-mode "js2-mode" nil t)
(add-to-list 'auto-mode-alist '("\\.js$" . js2-mode))Scope
lexical scope
Emacs の標準はダイナミックスコープ.
しかし, Emacs24 からは, ファイルの先頭に以下を書くと, setq で宣言した変数は本当の Lexical Scope になる.
-*- lexical-binding: t -*-高階関数を引数にしようとしたとき, M-x eval-buffer をすると評価できた.
局所変数
let, let*, letrec が利用できる.
関数型パラダイムで状態をもつ仕組み.
let*は haskell の do 記法に似ている.
ローカル関数の定義
let + lambda を利用する, 変数に無名関数を bind させることで実現する.
(let ((p (lambda (a) (message a))))
(funcall p "hoge"))letrec を利用する方が正式か?
letrec の rec は 再帰のこと. let は再帰関数が定義できないが, letrec はできる.
macro を利用
- cl-flet 関数再定義
- cl-labels ローカル関数
- cl-letf 関数の一時置き換え
- noflet
注意: flet, lables, letf は obsolite らしい
Elisp: 文字列
タイムスタンプ
いつも悩むやつ.
weeklyのフォーマット
(format-time-string "%Y-w%W")
(defun my/create-weekly-org-file (path)
(expand-file-name (format "%s.org" (format-time-string "%Y-w%W")) path))Emacs API
主に Emacs を操作するための関数をまとめる
ファイル操作
- concat: 文字列結合.
- file-truename: シンボリックリンクを絶対パスにする.
ポイント/カーソル移動
バッファの特別な位置を示す数値.
- (point): カレントバッファのポイントの取得.
- (point-min)/(point-max): カレントバッファの最小最大/ポイント取得.
- (goto-char p): ポイント位置へカーソル移動.
- (goto-line n): N行目に移動
- (beginning-of-line)/(end-of-line): 行頭/行末に移動.
カーソルの位置取得
things-at-point
編集
- (insert s): 文字列挿入.
コーディング規約
どうも暗黙の規約のようなものがあるように思えてならない.
規約? のようなものを書きためていくことにする.
ファイル形式
Yasnippet にする.
;;; filename --- description
;; Header ....
;;; Code:
(require 'foo)
(defgroup hogegroup nil
"Hoge in Emacs"
:prefix "hoge:"
:group 'hoge)
(defcustom hoge:xxx nil
"Hoge valuable"
:group 'hoge
:type 'string)
; ...
(defvar hoge:foo nil)
; ...
(defun hoge:reset ()
)
; ...
;;;###autoload
(defun hoge:hoge-start ()
"public functions"
)
; ...
(provide 'hoge)
;;; filename ends here;; filename --- desc
;; ヘッダ情報
記述方法ががここにまとまっている.
GNU Emacs Lisp リファレンスマニュアル: B. ヒントと慣習
;;; Code:
コードをここから書き始める.;;; Code:をつける
requiere
依存する elisp があれば, ここに書く.(目立つように)
defgroup
defcustom
autoload
ユーザに公開する関数は, ファイルの末尾に書き溜める.
書き始めには以下の宣言を書く.
;;;###autoloadprovide
ユーザがよみこむための宣言.
命名規約
Lisp 系言語は
- 小文字.
- 単語と単語の間は - をいれる.
indent
github のページが一番詳しい.
以下で揃える
- indent-region (C-M-\)
- lisp-indent-line (tab key)
- indent-sexp (C-M-q)
以下の英文記事からの抜粋.
Top-level functions
トップレベルの関数は 1 列目から開始.
Closing parentheses
カッコはまとめて閉じる. まとめて閉じないのは C 系の言語に慣れ親しんだ人のやることだ.
;;; bad
(defun f (x)
(when (< (g x) 3)
(h x 2)
)
)
;;; good
(defun f (x)
(when (< (g x) 3)
(h x 2)))Amount of indentation
indent のスペースは 2 つくらい.
;;; bad
(defun f (x)
(when (< (g x) 3)
(h x 2)))
;;; good
(defun f (x)
(when (< (g x) 3)
(h x 2)))Comments
シングルセミコロンは, コードに関する注意で コードと同ラインに書く.
(if (< (g x) 2) ; is it sufficiently small?
(top-level x) ; if so, abandon everything
(h y)) ; otherwise try again2 つのセミコロンは, 数行のコードにかかるコメント.
(when (< (g x) 2)
;; reinitialize and abandon everything
(setf *level-number* 0)
(top-level x))3 つのコメントは関数の説明時に利用.
;;; Compute the amount of space between symbols
;;; as a list of floating point values.
(defun compute-spaces (symbols)
(mapcar #'compute-single-space symbols (cdr symbols)))Indenting special forms
スペシャルフォームはそれぞれ決まった indent のルールがある.
-
Indenting the if special form
3 つの subexpressons をとる.
(if (= (f x) 4) (top-level x) (g x))
-
Indenting the when and unless special forms
はじめのラインは 条件判定にあたるので, はじめのラインに書く. 2 番目からのラインは, 条件判定ラインから 2 つ indent を下げて書く.
(when (= (f x) 4) (setf *level-number* 0) (unless *do-not-reinitialize* (reinitialize-global-information x) (reinitialize-local-information)) (top-level x))
-
Indenting the let and let* special forms
はじめのラインは変数の初期化で, 残りの部分がスペシャルフォームに当たる. 変数の初期化は, はじめのに書く. 残りのラインは 2indent 下げて書く.
(let* ((symbols (mapcar #'compute-symbol l)) (spaces (mapcar #'compute-space symbols (cdr symbols)))) (when (verify-spacing symbols spaces) (make-spacing permanent spaces)))
-
Indenting the do and do* special forms
loop の開始条件, 終了条件は列を揃える.残りの body は 2indent 下げる.
(do ((i 1 (1+ i)) (j (length l) (/ j 2))) ((= j 0) i) (iterate i j) (when (= (f x) 4) (setf *level-number* 0) (top-level x)))
デバッグ/ テスト
helpful
helpfulというパッケージをつかうと, 関数や変数のhelpが便利になる.
https://github.com/Wilfred/helpful
helpful-at-pointでその関数のhelpをみれる.
print debug
# This is useful for printing values
(messageg "Hello (%s)" foo)
# but doesn't work so well for data structures. For that, use
(prin1 list-foo)trace-function
関数のトレースを出す.
(defun f (x) (+ x 3))
(defun g (x) (+ (f x) 7))- M-x trace-function で f を選択.
- M-: (g 3) C-x C-e
trace-output buffer に出力結果がでる.untrace-all で解除.
再帰関数の確認に便利.
(defun fact (n)
(if (= n 0) 1
(* n (fact (1- n)))))
(fact 3)
;; 1 -> (fact 3)
;; | 2 -> (fact 2)
;; | | 3 -> (fact 1)
;; | | | 4 -> (fact 0)
;; | | | 4 <- fact: 1
;; | | 3 <- fact: 1
;; | 2 <- fact: 2
;; 1 <- fact: 6モード作成
minor-mode
define-minor-mode を利用して作成する.
オブション
- :lighter — the name, a string, to show in the modeline
- :keymap — the mode’s keymap
- :global — specifies if the minor mode is global (default nil)
Easy-Mmode
Emacs に default で入っている.
Bookmarks
簡単な例による説明.
- How to Make an Emacs Minor Mode « null program
- keyboard shortcuts - How to create keybindings for a custom minor mode in Emacs - Stack Overflow
📝Emacs Batch Mode
Emacs LispをScriptとして実行する方法.
Basics
—batchオプションでemacsを実行する.
emacs -batch -l ~/.emacs.editor --eval="(require 'foo)" \
--eval="(require 'bar)" \
--eval="(some-function $*)"- -batch: batchモードでEmacsを起動.
- -l: ロードするファイルを指定
- —eval: 評価.
- -f: 関数を実行
関数に引数を渡す
—eval=“(some-func $*)” のように, bashの引数をeval=()で包んで渡す. (-fではなく).
References
Emacs Lisp Tips
✅*scratch* バッファを利用する
なんか気軽に評価してみたいときは, defaultで作成される scratch バッファをつかうと便利.
References
- 逆引きEmacs Lisp - atwiki(アットウィキ)
- よくある処理を Emacs Lisp で書く場合 - Life is very short
- Emacs Lisp - Wikipedia
- Emacs-Lisp入門 2021