はじめに
SICP を Emacs Lisp でとくという, やや無謀なことに挑戦中.
SICP 自体は Scheme をベースにかかれているので, 例題や回答を elisp に置き換えながら読んでいく.
Emacs Lisp は不自由なもので, 結構ハマリポイントがあった.
第一章がようやく終わったところで, いったんたまったノウハウをはきだしておこう.
よくある置き換え
defun
毎回ある置き換え.
;; scheme
(define (hoge x) x)
;; emacs lisp
(defun hoge (x) x)
cond
cond の書き方について,
- scheme は 一番最後に else をつける.
- elips は 一番最後に t をつける.
(cond ((= 1 1) 1)
(else 2))
(cond ((= 1 1) 1)
(t 2))
高階関数
関数にの頭に ‘をつけて別の関数の引数にすると, 関数は評価されずに引数に渡すことができる.
評価するときは, (funcall f) のように funcall を呼ぶ.
ローカル変数
elips は scheme のように, 関数の中に関数をかいても 変数や関数のスコープを限定できない.
let, let*, letrec が利用をうまくつかって, ローカルな変数や関数をつかう.
ローカル関数の定義
let + lambda を利用する, 変数に無名関数を bind させることで実現する.
(let ((p (lambda (a) (message a))))
(funcall p "hoge"))
letrec を利用する方が正式か?
letrec の rec は 再帰のこと. let は再帰関数が定義できないが, letrec はできる.
落とし穴
Dynamic Scope
Emacs Lisp は Dynamic Scope という方式をとり, 名前空間がない. なので, 安直な a とか sum とかいう関数を名づけてしまうと, うっかり他の関数と競合する.
できるだけ, 他とかぶらない関数名をつけるほうが無難.
Emacs24 からは, ファイルの先頭に以下を書くと, setq で宣言した変数は本当の Lexical Scope になる.
-*- lexical-binding: t -*-
高階関数で 関数がわたせない
無名関数を入力しても, なぜか Symbol value as void となる.
以下を行頭に買いて, M-x eval-buffer で評価することで, 回避できた.
;; -*- lexical-binding: t -*-
max-lisp-eval-depth 発生
Emacs Lisp は 末尾最適化がされないため, 深い再帰処理をかくと, よくクラッシュする.
これによって, 解くのを諦めた問題多数. これが Elisp の限界か.
その他
自分一人で行き詰まった時は, 先人の知恵を拝借する.