GNU Emacs、Emacs Lisp 版 Minesweeper、Cocoa Emacs 日本語表示

最初に Emacs に触れたのは学生時代購入した X68000 上の Micro Emacs です。もしかしたら Sun のワークステーションの方が早かったかもしれません。インターネットもない時代だったので、本屋で立ち読みしたり、少ない小遣いで購入した書籍で、一生懸命操作を学びました。それ以来、あまり上達していません。X68000 の Micro Emacs では、便利な機能があらかじめ設定されていたので、ファンクションキーを多用していたのを覚えています。

今回、Learning GNU Emacs, Third Edition を読み終えたので、久しぶりに Parallels DesktopWindows XPX68000 エミュレータ XM6 を起動して Micro Emacs (em.x) を実行してみました。

もちろん廉価版なので、本家と違うところがいろいろあるのですが、今思うとすごく良くできていますね。ソースは覗いていませんが、OS に依存しているところは分離されているのかな?

Learning GNU Emacs, Third Edition を読んでる途中で

縦分割や

Window と Frame の違いも理解しました。恥ずかしながら、初めて知りましたが Meta Key として Alt (Option) キーも使えるんですね。Micro Emacs から使い始めたので、てっきり ESC キーだけかと思ってました。

Learning GNU Emacs, Third Edition を読みながら、ATOK Pad にメモした箇所がたくさんあります。ちょっとだけ、紹介しておきます。

GNU Emacs には、強力なヘルプがあります。

Help
C-h ?                   ヘルプ一覧
C-h a PATTERN    正規表現 PATTERN に一致する名前を持つコマンド一覧表示
C-h d PATTERN    PATTERN にマッチする関数、変数などを表示
C-h k

Window 操作も C-x と数字を入力するだけで、いろいろな動作をします。

Window
C-x 0       ウィンドウの削除
C-x 1       選択されているウィンドウ以外のウィンドウをすべて閉じる
C-x 2       ウィンドウを上下2つに分割する
C-x 3       ウィンドウを左右2つに分割する
C-x 4 f     他のウィンドウでファイルを開く
C-x 5 2    フレームの作成
C-x 5 o    他のフレームに移動

ディレクトリエディタ dired で、g キーを押せば、ディレクトリの内容を最新の状態に更新します。

dired
C-x d      dired 起動
u             フラグを外す (Remove mark)
d             削除フラグを付ける (Flag of delection)
x             削除フラグがついたファイルを削除 (Delete)       
g             ディレクトリ再読み込み (Immediate Refresh)
=            比較
C            指定したファイルをコピー (copy)
R            ファイル名変更 (Rename file)
v             View モードで表示 (View file, read-only)
s             ファイル名または日付でソート。トグル。
Enter      スクロール (Scroll down)
#            auto-save files にフラグを付ける
~            Backup files にフラグを付ける
* *          excitable files にフラグを付ける
L            ファイルを読み込む (Load file)
%m         正規表現に当てはまるファイル名にフラグを付ける

決まったファイルを編集するには Bookmark に登録しておくと便利です。

C-x r m  bookmark-set
C-x r l   List bookmarks

GNU Emacs の Window 上に shell も起動することができます。

M-x shell
M-p            最後のコマンドを表示
M−n           次のコマンドを表示

検索と置換には、正規表現も使えます。

検索
C-s
置換
M-%
SPACE    置換して次に移動
DEL        置換しないで次に移動
,             置換して結果を表示。移動はしない
正規表現検索
C-M-s
C-M-r
C-M-%

キーは割り当てられていませんが、指定した領域をコメントアウトするには

M-x comment-region
M-x uncomment-region

が便利です。C や Ruby など、ほとんどの言語が対応しています。

C-@ でマークし、カーソルキーで選択します。

M-x comment-region を実行すると、上図のように各行を /* */ でコメントアウトしてくれます。

コメントアウトを取り除くには、上図のように同じく領域を選択し、

M-x uncomment-region を実行します。

Learning GNU Emacs, Third Edition で、簡単な Emacs Lisp Programming も学んだので、どんな環境でも最初に挑戦するマインスイーパを作成してみたくなりました。Google で検索してみると

EmacsWiki: Mine Sweeper
http://www.emacswiki.org/emacs/MineSweeper

すでに存在するようです。私の理解の仕方が足りないのか、うまく動作させることができません。それで少し mine-sweeper.el を変更してみることにしました。まず、.emacs ファイルに

;; .========== .emacs ==========
;; load-path
(add-to-list 'load-path "~/lisp/")
;; minesweeper
(autoload 'mine-sweeper "mine-sweeper" nil t)
(autoload 'mine-auto "mine-sweeper-auto" nil t)
(add-hook 'mine-sweeper-mode-hook
(lambda ()
(define-key mine-sweeper-mode-map "a" 'mine-auto)))

を追加しました。ホームフォルダlisp フォルダを作成し、その中に mine-sweeper.el、mine-sweeper-auto.el ファイルをそれぞれ置きました。

mine-sweeper.el ファイルの mine-sweeper 関数から

(get-buffer-create "Mine")
(set-window-buffer (selected-window) "Mine")

をコメントアウトし

 (pop-to-buffer "*Mine*" nil)

を追加しました。mine-sweeper 関数は、下記のようになります。

;;; main
(defun mine-sweeper ()
"mine-sweeper
SPC  open
b    mark
m    mark
q    quit"
(interactive)
(setq max-lisp-eval-depth 3000)  ;for recursive call of open.           
(setq max-specpdl-size 3000)     ;for recursive call of open.
;;  (get-buffer-create "Mine")
;; (set-window-buffer (selected-window) "Mine")
(pop-to-buffer "*Mine*" nil)
(setq overwrite-mode t)
(setq major-mode 'mine-sweeper-mode
mode-name  "Mines")
(use-local-map mine-sweeper-mode-map)
(run-hooks 'mine-sweeper-mode-hook)
(mine-start)
(setq buffer-read-only t))

次に mine-clean-and-quit 関数を

(defun mine-clean-and-quit ()
(interactive)
;;  (switch-to-buffer-other-window "*Mine*")
(delete-window)
(kill-buffer "*Mine*"))

に変更しました。これで M-x mine-sweeper を実行すると、マインスイーパで遊ぶことができます。

最初にミニバッファ上で縦横のサイズ、地雷の数を入力します。( デフォルトの値を使用する場合は、Return キーを押すだけです。)
画面が表示されると

カーソルキー     セルを移動
スペースキー     セルをオープン
m キー              マーク
a キー               自動で解く

上記キー操作で遊ぶことができます。a キーを押すと mine-sweeper-auto.el の mine-auto 関数が呼ばれ、マインスイーパを自動で解いてくれます。人間が解くようにコンピュータが解くので、ある程度セルが開いている必要があるようです。条件によっては解けない場合があります。a キーを使って自動で解いた後にプログラムを終了させるとミニバッファに無意味な文字列が表示されます。私が、mine-sweeper.el を変更したのが原因のようです。

自作の Emacs Lispマインスイーパを作ってみたいですね。

GNU Emacs 上の日本語が綺麗に表示できるように、Google で検索していろいろ試しましたが、中々上手くいきません。結局、

IPAフォントのダウンロード || OSS iPedia
http://ossipedia.ipa.go.jp/ipafont/index.html

IPAフォントを使いました。表示結果は

のようになりました。.emacs ファイルは、下記リンクでダウンロードできるようにしておきました。よかったら使ってください。

GNU Emacs 設定ファイル (.emacs) サンプル:  emacs.zip (4K)
http://kyoshiaki.sakura.ne.jp/osx/Download/emacs.zip

Practical Common Lisp

NDA のため詳しくは書けませんが、最近 iPhone SDK に挑戦しています。まだ資料を読んでいるだけの段階ですが!何か作れたらいいなと思っています。

話は変わりますが、LISP 関連の書籍は今までに

入門Common Lisp―関数型4つの特徴とλ(ラムダ)計算

入門Common Lisp―関数型4つの特徴とλ(ラムダ)計算

On Lisp

On Lisp

の2冊購入しています。On Lisp は私にとっては難しすぎて、途中で読むのをやめてしまいました。基本的なところは 入門Common Lisp―関数型4つの特徴とλ(ラムダ)計算 で勉強したのですが、もう少し詳しい書籍が必要だと思っていました。しかし、Amazon で日本語の書籍を探してみたのですが、よさそうなものが見当たりません。最近、Appleの英語の資料を読み始め、英単語については、拙作 Wordbook2*1 で勉強しているので、洋書を読んでみようと思い立ちました。

それで

Practical Common Lisp (Books for Professionals by Professionals)

Practical Common Lisp (Books for Professionals by Professionals)

を買ってみました。購入して正解でした。

本当に丁寧に説明してあります。Optional Parameter (&optional)、Rest Parameter(&rest)、Keyword Parameter(&key) についての説明もあります。

実行環境は [id:KYoshiaki:20070225] と同じく Carbon Emacs を用いました。今回は Emacs Lisp (Elisp) ではなく Common Lisp を使う必要があります。それで書籍に紹介されている sbcl、SLIME を利用することにしました。

Leopard 以前は Fink を利用していたのですが、これを機会に MacPorts に変更することにしました。

The MacPorts Project -- Home
http://www.macports.org/
MacWiki - MacPorts
http://macwiki.sourceforge.jp/wiki/index.php/MacPorts

を参考に設定した後、パッケージ sbcl をコマンド

~ $ sudo port install sbcl

を使ってインストールしました。

また Carbon Emacs に表示されている SHA-1 の確認のために

~ $ sudo port install coreutils

を使ってパッケージ coreutils をインストールしました。
このパッケージには md5、md5sum、sha1sum などのコマンドが含まれています。ただし接頭語として g が追加されています。(gmd5、gmd5sum、gsha1sum というコマンド名になります。)

port コマンドに次の引数を渡すと、どのパッケージに含まれているか確認できます。

~ $ port provides /opt/local/bin/gsha1sum
/opt/local/bin/gsha1sum is provided by: coreutils

そして

Carbon Emacs パッケージ
http://homepage.mac.com/zenitani/emacs-j.html
Emacs をダウンロード (2008年春版; 40.8 MB)
SHA1: be4d67c6d44d937a7525fc3a8a739c443021ae29

からダウンロードしたファイル CarbonEmacs-Tiger-20080405.dmgSHA-1 を gsha1sum コマンドで求めると

~/Downloads $ gsha1sum CarbonEmacs-Tiger-20080405.dmg
be4d67c6d44d937a7525fc3a8a739c443021ae29  CarbonEmacs-Tiger-20080405.dmg

と表示されます。 私は SHA-1 が等しいか判断するためにスクリプト compare.sh

 $ cat ./compare.sh
#!/bin/sh
MD1="$1"
MD2="$2"
if [ $MD1 = $MD2 ] ; then
echo "match"
else
echo "failure"

を使っています。基本的にUnix についての知識は乏しいので、もっと良い方法があるかもしれません。

ただ単純に

Carbon Emacs パッケージ
http://homepage.mac.com/zenitani/emacs-j.html
Emacs をダウンロード (2008年春版; 40.8 MB)
SHA1: be4d67c6d44d937a7525fc3a8a739c443021ae29

に表示されている SHA-1 と gsha1sum で求めた SHA-1 をコピーして、コマンド compare.sh の引数として渡し

~ $ ./compare.sh be4d67c6d44d937a7525fc3a8a739c443021ae29  be4d67c6d44d937a7525fc3a8a739c443021ae29
match
~ $ 

match と表示されれば成功です。

SLIME については

SLIME: The Superior Lisp Interaction Mode for Emacs
http://common-lisp.net/project/slime/

から slime-2.0.zip をダウンロードしました。ファイル slime-2.0.zip を展開して作成された “slime-2.0” フォルダをホームフォルダに移動し、同封の ReadMe を参考に .emacs ファイルを

 (add-to-list 'load-path "~/slime-2.0/")  ; your SLIME directory
(setq inferior-lisp-program "/opt/local/bin/sbcl") ; your Lisp system
(require 'slime)
(slime-setup)

と設定しました。

Carbon Emacs 上で `M-x’ slime ( ESC-x slime ) と入力すると SLIME が起動します。
カーソルをファイル内の評価したい式に移動し C-c C-c と押せば実行できます。

現在、

Practical Common Lisp (Books for Professionals by Professionals)
Peter Seibel 著
CHAPTER 7 MACROS: STANDARD CONTROL CONSTRUCTS
DO

を読んでいる途中です、[id:KYoshiaki:20070225] で書いたフィボナッチ数列

F_{n}=\left\{{1 \text{ \(n=1, 2\)}\atop{F_{n-1} + F_{n-2} \text{ \(n\geq 3\)}}}\right.

もでてきます。

Practical Common Lisp (Books for Professionals by Professionals) の例

(do ((n 0 (1+ n))
(cur 0 next)
(next 1 (+ cur next)))
((= n 10) cur))

入門Common Lisp―関数型4つの特徴とλ(ラムダ)計算 の例

(defun fib (n)
(cond ((= n 1) 1)
((= n 2) 1)
(t (+ (fib (- n 1)) (fib (- n 2)))))) 

動作を説明しやすいように n = 5

(do ((n 0 (1+ n))
(cur 0 next)
(next 1 (+ cur next)))
((= n 5)
(format t "~%~d" cur)))

とします。

実行結果は

5
CL-USER> 

となります。入門Common Lisp の fib を使っても

CL-USER> (fib 5)
5

と同じ値になります。

変数の移り変わりを表で表すと

n cur next
0 0 1
1 1 1
2 1 2
3 2 3
4 3 5
5 5 8
n cur next
0 F_{0} F_{1}
1 F_{1} F_{2}
2 F_{2} F_{3}
3 F_{3} F_{4}
4 F_{4} F_{5}
5 F_{5} F_{6}

ただし F_{0} = 0 とする。

になります。

また、CHAPTER 7 の最後に出てくるフィボナッチ数列を求める関数の動作も表にしてみました。
CHAPTER 22 を読んで LOOP についてだいたい理解しましたが、finally の挙動については自信がありません。

(format t "~%~d"
(loop for i below 5
and a = 0 then b
and b = 1 then (+ b a)
finally (return a)))
i a b
0 0 1
1 1 1
2 1 2
3 2 3
4 3 5
finally 5 5 8

実行結果

5
CL-USER> 

特に for と and の違いを理解するにに苦労しました。

(loop for var = initial-value-form [ then step-form ]  ...)

for を利用する場合

(format t "~%~a"
(loop for i below 5
for x = 0 then y
for y = 1 then (+ x y)
collect y))
i x y
0 0 1
1 1 2
2 2 4
3 4 8
4 8 16

実行結果

(1 2 4 8 16)
CL-USER> 

and を利用する場合

(format t "~%~a"
(loop for i below 5
and x = 0 then y
and y = 1 then (+ x y)
collect y))
i x y
0 0 1
1 1 1
2 1 2
3 2 3
4 3 5

実行結果

(1 1 2 3 5)
CL-USER> 

*1:Wordbook2: まだ公開していません。取扱説明書を作成中です。