.emacsの雛形を公開します。Emacs23以上対応です。以前、UbuntuのEmacs22辺りまではUbuntu Japanese Team制作の
dot.emacs.exampleが同梱されていたのですが、Emacs23からは完全UTF-8対応になった為か、無くなってしまいました。そこで利便性を考え、オリジナル(GPLライセンス)の
dot.emacs.exampleを改造したのが以下のdot-emacs-example.elです。勝手に持って行って下さい(笑)。
Paul Graham と Eric S. Raymond に騙され続けた男の奮闘記
.emacsの雛形を公開します。Emacs23以上対応です。dot.emacs.exampleが同梱されていたのですが、Emacs23からは完全UTF-8対応になった為か、無くなってしまいました。dot.emacs.exampleを改造したのが以下のdot-emacs-example.elです。
;; -*- Emacs-Lisp -*-
;; るねきちモード Version 1 by りう, fixed by lune
(defvar lune-mode-map (make-keymap))
(let ((key ?a))
(while (<= key ?z)
(define-key lune-mode-map (char-to-string key) 'i-am-lune)
(setq key (+ 1 key))))
(defun lune-mode ()
"るねきちモードだよー!"
(interactive)
(setq major-mode 'lune-mode)
(setq mode-name " るねきちモード ")
(use-local-map lune-mode-map))
(defun i-am-lune ()
(interactive)
(insert " 僕るねきちナリ "))
keyと言う形でいきなり変数指定をすると、それは大域変数として解釈されてしまうので名前空間を汚してしまう。letを使って局所変数として宣言した方が良い。
(let ((key ?a))
(while (<= key ?z)
(define-key lune-mode-map (char-to-string key) 'i-am-lune)
(setq key (+ 1 key))))
letなんて使うだろうか・・・?defineなんて付いてる為、ついつい脊髄反射的に「マクロ」であるとか、「特殊形式」っぽく捉えちゃいそうになりますが、そーじゃなくって、単なる関数です。(require 'cl)を用いてもうちょっとCLerなりSchemerが納得しやすいコードは以下のようになるでしょう。
;; -*- Emacs-Lisp -*-
;; cametan-mode Version 1
(require 'cl)
(defvar *cametan-mode-map* (make-keymap))
(mapcar #'(lambda (x)
(define-key *cametan-mode-map*
(char-to-string x) 'i-am-cametan))
(loop for key from ?a to ?z collect key))
(defun cametan-mode ()
"This is cametan-mode!"
(interactive)
(setf major-mode 'cametan-mode
mode-name "cametan-mode")
(use-local-map *cametan-mode-map*))
(defun i-am-cametan ()
(interactive)
(insert " I am cametan "))
letを使うより、こっちの方がCLerやSchemerは納得しやすいでしょう。mapcarを用いてloop ~ collectで作り出した文字コードのリストへ写像するcollectキーワードを使っています。loop構文も大した事ないわけですよ。むしろ、Pythonのリスト内包表記程度、には軟弱に使えます(笑)。
【問】前問「るねきちモード」を以下のように改良した「るねきちモードII」を作成せよ。
- a~zの押すキーに対応して「るねきちAナリ」~「るねきちZナリ」を挿入するように変更せよ。
- さらに、a~zどれか1つのキーを押すと画面を3回フラッシュし、「自爆」というメッセージを表示して、バッファの内容を消去する機能を付け加えよ。バッファ内容を消去する関数は
(erace-buffer)である。
;; -*- Emacs-Lisp -*-
;; るねきちモード Version 2 by lune
(defvar lune-mode-map (make-keymap) " るねきちモードのキーマップ ")
;; とりあえず a ~ z までのキーマップ
(let ((key ?a))
(while (<= key ?z)
(define-key lune-mode-map (char-to-string key) 'lune-i-am)
(setq key (+ 1 key))))
;; 乱数で自爆キーを決めるぞー
(let* ((jibaku (random 26)) ;0~25の乱数
(key (char-to-string (+ ?a jibaku))))
(define-key lune-mode-map key 'lune-jibaku))
;; ここから本体
(defun lune-mode ()
"るねきちモードだよー!"
(interactive)
(setq major-mode 'lune-mode
mode-name " るねきちモード ")
(use-local-map lune-mode-map))
(defun lune-i-am () ;パッケージ共通の接頭辞を持つように
(interactive) ;変えてみた
(insert (format " るねきち%sナリ "(this-command-keys))))
(defun lune-jibaku ()
"自爆関数"
(interactive)
(let ((visible-bell t))
(ding)
(sleep-for 1)
(ding)
(sleep-for 1)
(ding)
(sleep-for 1)
(erase-buffer)
(message "自爆!")))
(require 'cl)組み入れて書き直してみたいと思います。
;; -*- Emacs-Lisp -*-
;; cametan-mode Version 2
(require 'cl)
(defvar *cametan-mode-map* (make-keymap) "keymap of cametan-mode")
;; 自爆キーの設定
(defvar *jibaku* nil) ;大域変数 *jibaku* の初期値
;; setf で *jibaku* を乱数を使って再定義しなおすと
;; バッファが評価される度に値が更新される
(setf *jibaku* (+ ?a (random 26)))
;; a ~ z までdefine-key
(mapcar #'(lambda (x)
(define-key *cametan-mode-map*
(char-to-string x)
(if (char-equal x *jibaku*)
'cametan-jibaku
'cametan-i-am)))
(loop for key from ?a to ?z collect key))
;; ここから本体
(defun cametan-mode ()
"This is cametan-mode!"
(interactive)
(setf major-mode 'cametan-mode
mode-name "cametan-mode")
(use-local-map *cametan-mode-map*))
(defun cametan-i-am () ;パッケージ共通の接頭辞を持つように
(interactive) ;変更
(insert (format " I am cametan %s " (this-command-keys))))
(defun cametan-jibaku ()
"自爆関数"
(interactive)
(dotimes (n 3 (progn (erase-buffer)
(message "自爆!")))
(let ((visible-bell t))
(ding)
(sleep-for 1))))
jibakuをローカル変数として定義したのは、恐らく、変数名とするkeyが被らないようにする為だけではないか、と。let*なんて持ち出さなくても、大域変数として*jibaku*を定義しちゃった方が、構造的には簡単に思えます。mapを使ったヴァージョンと同じです。*jibaku*を利用した条件分岐で分けています。それだけ。do系の構文に切り替えた方が得策だと思います。doの本体部は暗黙のprogn(あるいはbegin)がありますし、副作用系は特に無造作に放り込んでおけます。dotimesが便利です。loop構文だと大掛かりになって適さないのでは、とか思います。
;; loop を使って書き直してみた自爆関数の例
;; コード的には dotimes に比べると
;; 大げさに見える
(defun cametan-jibaku ()
"自爆関数"
(interactive)
(loop repeat 3
do (let ((visible-bell t))
(ding)
(sleep-for 1))
finally (progn (erase-buffer)
(message "自爆!"))))
visible-bellってのが変数っぽい辺りが良く分からず、それが成立しているスコープの範囲内じゃないと(ding)が動かない、と言う良く分からん仕様になってるんですね。
「何じゃこりゃ?」
【問】a~zのどのキーを押しても"僕るねきちナリ"が挿入される「るねきちモード」を作成せよ。
;; -*- 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 " 僕るねきちなり "))
(require 'cl)と書いているケースが多い、です。
;; -*- 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"))
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))
iota辺りが無いと辛い。最初に転写されるリストを生成しなきゃなりませんので。loopは短く書けて便利です。M-x cametan-modeで動作を確認する事が出来ます。