Lisp 開発環境
ILISP - emacs + ilisp
編集とか
こんなコードがあったとする.
;; -*- mode: lisp -*-
(defun range (n &key (beg 1) (step 1))
(let ((acc nil))
(dotimes (i n (reverse acc))
(push (+ (* i step) beg) acc))))
S 式用の,Emacs の標準機能を以下に示す.
(*移動*) ESC C-a : beginning-of-defun : defun の先頭へ移動 ESC C-e : end-of-defun : defun の末尾へ移動 ESC C-f : forward-sexp : カーソル位置から始まる S 式の末尾に移動 ESC C-b : backward-sexp : カーソル位置より一つ後の S 式の先頭に移動 ESC C-d : down-list : 1 レベル内側の S 式へ移動 ESC C-u : backward-up-list : 1 レベル外側の S 式へ移動 ESC C-n : forward-list : 括弧のグループを超えて前方へ移動 ESC C-p : backward-list : 括弧のグループを超えて後方移動 ESC C-k : kill-sexp : カーソル位置から始まる S 式を kill する ESC C-backspace : backward-kill-sexp : カーソル位置より一つ後の S 式を kill する (*インデント*) 割り当て無し : indent-sexp : S 式内を indent する (*マーク*) ESC C-SPC : mark-sexp : 引数の数だけ S 式をマークする ESC C-h : mark-defun : defun 全体をマークする (*編集*) ESC C-t : transpose-sexps : ポイント位置前後の S 式を入れ換える C-x n d : narrow-to-defun : defun の範囲に narrow する C-x n w : widen : narrow-to-defun を解除
のように,標準で S 式を編集する為のコマンド群が揃っている.
S 式単位の移動を行なっている画面写真を以下に示す.
S 式単位の移動
ポイント位置から C-u ESC C-SPC で S 式 2 つ分マークしたのち,C-xC-x で移動
S 式単位のマーク
このように,Emacs は標準で S 式単位の操作を行なえる.こういった機能を使いこなせば, 一見とっつきにくい S 式でも快適に作業がおこなえる.特に「S 式」という単位で移動できる というのは Lisp の括弧の特権であろう.括弧の対応によって簡単に要素を移動できる!! 中置記法とちがって優先度で頭を捻る必要もない. (とかいっときながら,Python も使ってるけど…あれは括弧が無いのが良い.)
ヘルプとかドキュメントとか
ilisp から利用可能な 1 ドキュメントとしては,
- CLtL2 (Common Lisp The Language 2nd Edition)
- HyperSpec (Commmon Lisp Hyper Spec)
が ilisp からヘルプとして利用可能です.HyperSpec のが新しい規格書です が,ドキュメントやサンプルが沢山あったほうが嬉しいので CLtL2 も入手しておき ましょう .
ilisp の設定を済ませると,コマンド一発で CLtL2 や HyperSpec が引けるように なります.HTML を表示時に外部ブラウザが起動してうっとおしい人は lynx や w3m を使 えば Emacs 内で全てが完結します.
また,通常の ilisp では関数名を入力した後のスペースで Window がポップアップ して引数リストを表示してくれますが,これが邪魔という人は
(setf lisp-no-popper t)
とすればポップアップしなくなります.さらに,引数をミニバッファに表示するのす ら邪魔だ,という人は
(setf ilisp-*arglist-message-lisp-space-p* nil)
で表示しないように設定できます.
ilisp 環境では
- 調べたいシンボルにカーソルをあわせる
- hyperspec-lookup コマンド一発
で,簡単にヘルプが引けます.
[1] ちなみに,デフォルトではネットワーク経由で使用するように設定されていますので,ネットワークに繋っている環境では何も考えずに利用できます.ネットワークに常時繋がっていない人はローカルにダウンロードして common-lisp-hyperspec-root を設定しましょう.
ilisp の機能とか
とりあえず,現状使ってる ilisp の機能.(broken?) は設定が悪いのかちゃんと動 いてないやつ.(だれか教えて下さい…)
PREFIX : C-c C-c C-s slow-lisp 安全第一に C-c C-f fast-lisp 速度重視で C-c L cltl2-lookup CLtL2 を引く C-c H hyperspec-lookup HyperSpec を引く C-c k compile-file-lisp ファイルをコンパイル C-c l load-file-lisp ファイルをロード C-c S select-ilisp lisp を切り替える(複数の Lisp 処理系を使ってる時とか) C-c s status-lisp ilisp の状態を表示 C-c g abort-commands-lisp lisp に送ったコマンドを中断 C-c z reset-ilisp lisp の状態をリセット C-c y call-defun-lisp (broken?) C-c b switch-to-lisp lisp なバッファに切り換え C-c * Prefix Command C-c SPC mark-change-lisp 変更マークを付ける C-c ! default-directory-lisp lisp のデフォルトディレクトリを表示 C-c t trace-defun-lisp 関数のトレース ON/OFF C-c C-w compile-region-and-go-lisp リージョンをコンパイル後,lisp バッファへ移動 C-c C-n eval-next-sexp-and-go-lisp S 式を評価後,lisp バッファへ移動 C-c C-e eval-defun-and-go-lisp 関数定義を評価後,lisp バッファへ移動 C-c C-r eval-region-and-go-lisp リージョンを評価後,lisp バッファへ移動 C-c c compile-defun-lisp 関数定義をコンパイル C-c C-b ilisp-compile-buffer バッファにあるコードをコンパイル C-c w compile-region-lisp リージョン内のコードをコンパイル C-c n eval-next-sexp-lisp 次の S 式を評価 C-c e eval-defun-lisp 関数定義を評価 C-c r eval-region-lisp リージョンを評価 C-c ^ edit-callers-lisp (broken?) C-c M macroexpand-lisp マクロ展開 C-c m macroexpand-1-lisp マクロ展開(macroexpand-1 ふつーこっち) C-c d documentation-lisp ドキュメント表示 C-c a arglist-lisp 引数リスト表示 C-c I inspect-lisp インスペクタ表示 C-c i describe-lisp describe する C-c ) find-unbalanced-lisp 括弧の不一致を見つける C-c ; comment-region-lisp リージョンをコメントアウト C-c <return> complete 補完 C-c TAB complete-lisp lisp シンボルを補完 C-c C-c compile-defun-and-go-lisp 関数定義をコンパイル後,lisp なバッファに移動 C-c C-z run-lisp lisp を起動 ESC RET complete 補完 ESC TAB complete-lisp lisp シンボルを補完 ESC ` next-caller-lisp (broken?) ESC " replace-lisp 置換 ESC ? search-lisp (broken?) ESC . edit-definitions-lisp (broken?) ESC , next-definition-lisp (broken?) ESC C-e end-of-defun-lisp 関数定義の末尾へ移動 ESC C-a beginning-of-defun-lisp 関数定義の先頭へ移動 ESC C-q indent-sexp-ilisp S 式内をインデント ESC C-r reposition-window-lisp 位置を再設定 ESC q reindent-lisp 再インデント ESC C-x eval-defun-lisp 関数定義を評価 C-c * 0 clear-changes-lisp 変更マークをクリア C-c * c compile-changes-lisp 変更マークしたところをコンパイル C-c * e eval-changes-lisp 変更マークしたところを評価 C-c * l list-changes-lisp 変更マークのリスト表示
SLIME - emacs + slime
SLIME について
http://common-lisp.net/project/slime/ から入手可能.いろいろと面白い機能があ る.メインの環境が CMUCL なこともあって,最近は ILISP から SLIME に乗りかえ 気味.現在は CMUCL, SBCL, OpenMCL といった処理系に対応.まだ正式公開以前の版 であるため,CVS の先端を取ってくるとたまに壊れているときがある.
SLIME の特徴は,
- 別プロセスで起動した lisp 処理系とソケット経由で通信する
- コマンド一発で HyperSpec が引ける
- コンパイラの note, warning, error をソース上で示してくれる(下線が引かれて,カーソルを合わせると詳細がでる)
- 親切なデバッガ(バックトレース上でカーソルを合せると詳細が表示されたりとか)
- 現在のパッケージを表示してくれる SLIME 独自の repl ができた
SLIME のセットアップ
README によれば,ダウンロードした slime を展開したら,
(add-to-list 'load-path "/the/path/to/this/directory")
(require 'slime)
(add-hook 'lisp-mode-hook (lambda ()
(slime-mode t)
(show-paren-mode)))
とするだけ.Common Lisp らしいインデントにするには↓をどーぞ.(私の趣味ですが)
(add-hook 'slime-mode-hook
(lambda ()
(setq lisp-indent-function 'common-lisp-indent-function)))
;; Additional definitions by Pierpaolo Bernardi.
(defun cl-indent (sym indent)
(put sym 'common-lisp-indent-function
(if (symbolp indent)
(get indent 'common-lisp-indent-function)
indent)))
(cl-indent 'if '1)
(cl-indent 'generic-flet 'flet)
(cl-indent 'generic-labels 'labels)
(cl-indent 'with-accessors 'multiple-value-bind)
(cl-indent 'with-added-methods '((1 4 ((&whole 1))) (2 &body)))
(cl-indent 'with-condition-restarts '((1 4 ((&whole 1))) (2 &body)))
(cl-indent 'with-simple-restart '((1 4 ((&whole 1))) (2 &body)))
SLIME のキーバインド
slime.el より.
'((" " . slime-space) ;; 関数名の後等でミニバッファに情報を表示するためのもの.
("\M-p" . slime-previous-note) ;; 注釈(コンパイル時等のエラーや警告)にジャンプできる.
("\M-n" . slime-next-note) ;; 次の注釈にジャンプできる.
("\C-c\M-c" . slime-remove-notes) ;; 注釈を消す
("\C-c\C-k" . slime-compile-and-load-file) ;; カレントバッファと対応するファイルをコンパイルした後ロードする
("\C-c\M-k" . slime-compile-file) ;; ファイルをコンパイル
("\C-c\C-c" . slime-compile-defun) ;; 関数定義をコンパイル
("\C-c\C-l" . slime-load-file) ;; ファイルをロード
;; Multiple bindings for completion, since M-TAB is often taken by
;; the window manager.
("\M-\C-i" . slime-complete-symbol) ;; シンボル補完
("\C-c\C-i" . slime-complete-symbol) ;; シンボル補完(M-TAB が Window Manger に食われちゃってる場合)
("\M-." . slime-edit-fdefinition) ;; 関数定義へジャンプ
("\M-," . slime-pop-find-definition-stack) ;; M-. の移動を逆にたどる
("\C-x\C-e" . slime-eval-last-expression) ;; 式を評価する
("\C-c\C-p" . slime-pprint-eval-last-expression) ;; 式を評価して結果を表示
("\M-\C-x" . slime-eval-defun) ;; 関数定義を評価する
("\C-c:" . slime-interactive-eval) ;; ミニバッファにプロンプトを出し,入力された式を評価
("\C-c\C-z" . slime-switch-to-output-buffer) ;; slime の出力バッファに移動
("\C-c\C-d" . slime-describe-symbol) ;; シンボルを describe した結果を表示
("\C-c\M-d" . slime-disassemble-symbol) ;; シンボルを disassemble した結果を表示
("\C-c\C-t" . slime-toggle-trace-fdefinition) ;; 関数の TRACE を ON/OFF する
("\C-c\C-a" . slime-apropos) ;; apropos
("\C-c\M-a" . slime-apropos-all) ;; ↑との違いが??
([(control c) (control m)] . slime-macroexpand-1) ;; マクロを展開 (macroexpand-1)
([(control c) (meta m)] . slime-macroexpand-all) ;; マクロを展開 (macroexpand)
("\C-c\C-g" . slime-interrupt) ;; SLIME に割り込む
("\C-c\M-g" . slime-quit) ;; SLIME を終了
("\C-c\M-0" . slime-restore-window-configuration) ;; ?
("\C-c\C-h" . hyperspec-lookup) ;; HyperSpec を引く
("\C-c\C-wc" . slime-who-calls) ;; ?
("\C-c\C-wr" . slime-who-references) ;; ?
("\C-c\C-wb" . slime-who-binds) ;; ?
("\C-c\C-ws" . slime-who-sets) ;; ?
("\C-c\C-wm" . slime-who-macroexpands) ;; ?
;; Not sure which binding is best yet, so both for now.
([(control meta ?\.)] . slime-next-location) ;; ?
("\C-c\C- " . slime-next-location) ;; ?
("\C-c~" . slime-sync-package-and-default-directory) ;; パッケージとディレクトリをファイルに合わせる
xyzzy - Common Lisp なエディタ
Windows で Lisp と言えばこれ.xyzzy が素晴しいです. なんと Common Lisp (準拠度 6 割程度とのこと)を採用した高性能エディタです.
設定
xyzzy は標準で lisp モードを持っていますが,何故かキーワードハイライトが OFF になってます. キーワードファイル自体は etc/lisp に存在しているので,lisp-mode のフックで有効にしてやれば OK です.
(add-hook '*lisp-mode-hook*
#'(lambda ()
(make-local-variable 'keyword-hash-table)
(setf keyword-hash-table (load-keyword-file "lisp"))
(make-local-variable 'regexp-keyword-list)
(setf regexp-keyword-list
(compile-regexp-keyword-list
'(("(" t (:color 14))
(")" t (:color 14)))))))
また,Common Lisp で開発する時に必須の HyperSpec を引けるように hyperspec.el を Emacs から移植しました.




