2010年1月15日金曜日

メジャーモードを作ろう!

以前からEmacs Lispに興味があったわけですけど、やっと勉強をはじめたい、と思います。

Lispで実用的なプログラムを書くのは、識者の意見はともかくとして、非常に難しい、と言う事が分かってきました。
現時点では、一般的な感覚で言うと、

  • 実用的なプログラム=GUIでのプログラム


って事なのです。
はいそこ。逆らわない。識者、あるいはハッカーの「極端な」意見なんてどーでも良い、のです。
如何に素晴らしいプログラムが書けようと、コマンドラインのプログラムだったら

「何じゃこりゃ?」

って言われるのがオチ、です。
そして、プログラムは「広く使ってもらわなければ」意味が無いのです。

一般に、マジメなプログラミングの勉強として考えると、Emacs Lispは避けられる傾向があります。
と言うのも、現代的なLispではないので、動的スコープしか持ってません。
しかし、GUIがどーの、って話で考えてみると、既にEmacsは「膨大な」フレームワークを内包している。自分で一から作らなくて良い。
本来の「Emacs拡張用言語」の枠組みを超えて、古き良きBASIC的プログラミング言語として楽しむのなら、依然としてEmacs Lispは強力でしょう。何せ、元々、OSから浮きまくってるEmacsは、まさしく「環境」として捉えられるので、一種の仮想マシンとして考えられるから、です。
ゲームなんかでも、GUIをLispで一から組み上げるのは大変そうですが、一方、Emacs上なら実現は簡単ぽく思えます。
この際、出来たものがショボくてもそれは問題じゃあないのです。昔のApple II的なプログラミングは、Emacs上でこそ実現すべきじゃないのか。そんな事を思っている、のです。
Emacsは21世紀のApple IIになるべきだ、とさえ思っています。




と言うわけで、やさしいEmacs‐Lisp講座の章末問題を中心にしてメモって行きたいと思います。

【問】a~zのどのキーを押しても"僕るねきちナリ"が挿入される「るねきちモード」を作成せよ。

やさしいEmacs‐Lisp講座で紹介されている模範回答例は以下のものです。

;; -*- Emacs-Lisp -*-
;; るねきちモード Version 0 by ゆう
(defvar lune-mode-map (make-keymap))

(defun lune-mode ()
"るねきちモードだよー!"
(interactive)
(setq major-mode 'lune-mode)
(setq mode-name " るねきちモード ")
(setq key 97)
(while (<= key 122)
(define-key lune-mode-map (char-to-string key) 'i-am-lune)
(setq key (+ 1 key)))
(use-local-map lune-mode-map))

(defun i-am-lune ()
(interactive)
(insert " 僕るねきちなり "))

多分、Schemeやってる人は相当違和感覚えるコーディングスタイルなんですよね、これは(笑)。
最近では、S式でのプログラミング=関数型プログラミング、って解釈される傾向があるんで、かなり違和感を覚えるスタイルです。基本S式を並べただけで、やってることはC言語的な「手続き型」のプログラミングです。
ただ、いわゆるScheme型のプログラミングとは違い、ネストが浅いんで、「見やすい」とは言えますね。この辺敷居が高くないんで、Emacs Lispプログラミングを愛好する人が多いのでしょうか(一般に、関数型プログラミングスタイルを徹底すればするほど、ネストが深くなっていく傾向がアリ)。
また、結構馴染みが無い関数名も多いです。この辺はEmacsと言うテキストエディタと「密接な」関係にあるEmacsならでは、ですよね。
馴染みが無い関数をリファレンスにリンク貼っておきましょうか。

ブログって便利だな(笑)。メモ取るのにリンクするだけで済むし。
何で今までこう言うのを効率的に使わなかったのかしら。

ソースコードを見ると、どれが大域変数でどれが局所変数なのか、つい考え込んでしまいます。
でもダイナミックスコープのLispなんで関係無いんですがね(爆)。
しかし、どれが既存の変数でどれが新規に作った変数なのか分かり辛いのは確か、です。
*scratch*バッファで調べた結果、major-modemode-nameってのが既存の変数ですよね。よって、これらは特に名前自体を変更する事は出来ないでしょう。
しかしながら、mode-map関連のはいわゆる自由な大域変数なのかな?CLでは明示的な大域変数宣言では、*(スター)で変数名を挟む、と言う命名規約があります。

ところで、最近のelispファイルを見ると、冒頭で(require 'cl)と書いているケースが多い、です。
これはEmacs Lispでもうちょっと現代的なCommon Lispの機能を使える為のオマジナイです。
これを使ってお題のコードをもうちょっと現代的に書き直してみたいと思います。


;; -*- Emacs-Lisp -*-
;; cametan-mode Version 0
(require 'cl)

(defvar *cametan-mode-map* (make-keymap))

(defun cametan-mode ()
"This is cametan-mode!"
(interactive)
(setf major-mode 'cametan-mode
mode-name "cametan-mode")
(loop for key from 97 to 122
do (define-key *cametan-mode-map* (char-to-string key) 'i-am-cametan))
(use-local-map *cametan-mode-map*))

(defun i-am-cametan ()
(interactive)
(insert "I am cametan"))


大分現代的なCLっぽいコードになったと思うんですけど。

第一に、そもそも、古いLispと違って、Emacs Lispでもsetq一つで複数の変数をバインドできると思うんですけど。
複数setqを書きまくるのもアレなんで、纏めた方が良い、と思いました。
ここでは、(require 'cl)を用いて、setqの代わりにsetfを用いています。

第二に、原版のコードでは(setq key 97)としてからwhile使ってdefine-keyでキーを定義、(setq key (+ 1 key))してバインドしてますが、CLerだったらloop使って済ます気がします。
ここ、ですよね。

(loop for key from 97 to 122
do (define-key *cametan-mode-map* (char-to-string key) 'i-am-cametan))

Schemerだったら高階手続きであるmapで同様の処理をしたいトコでしょうけど、そもそもSRFI-1iota辺りが無いと辛い。最初に転写されるリストを生成しなきゃなりませんので。
また、キーに機能を保持させる以上、要請は「破壊的作用」を伴います。その要点を最初からクリアするなら、CL的な「破壊的な繰り返し」を用いた方がベターだ、と言う判断ですね。
こう言う場合、意外とCLのloopは短く書けて便利です。

バッファを評価したあと、M-x cametan-modeで動作を確認する事が出来ます。

0 件のコメント:

コメントを投稿