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 を利用

注意: 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

ユーザに公開する関数は, ファイルの末尾に書き溜める.

書き始めには以下の宣言を書く.

;;;###autoload

provide

ユーザがよみこむための宣言.

命名規約

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 again

2 つのセミコロンは, 数行のコードにかかるコメント.

(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をみれる.

# 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

簡単な例による説明.

📝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