Hatena::Groupcadr

kozima の日記

2011-04-25

auto compression

| 00:35

削れる空白やコメントを削除するコマンド。たいていのコードは限界まで縮められると思います。

バイト数と Statistics を表示する機能を追加して,ついでに gist に置きました。

なんか引数の渡し方が変なところがあるのに書いた後で気付いた。

2011-04-03

emacs から StumpWM の eval を呼ぶ

| 11:28

.stumpwmrc を編集しているときに,とりあえず書いてみたものを評価したいと思うことがあったのでできるようにしました。

文字列を投げつける部分

stumpish を使うとよいようなので shell-command で呼び出してみました。結果は短ければミニバッファに,長ければ *Shell Command Output* バッファに現れるようです。

(defun mylib-stumpwmrc-eval-region (start end)
  (interactive "r")
  (let ((string (buffer-substring-no-properties start end)))
    (shell-command (format "echo %S | stumpish -e eval"
                           (format "(progn %s)" string)))))

(defun mylib-stumpwmrc-eval-last-sexp ()
  (interactive)
  (save-excursion
    (let ((start (progn (backward-sexp 1) (point)))
          (end (progn (forward-sexp 1) (point))))
      (mylib-stumpwmrc-eval-region start end))))

あんまりテストしてないのですが,C-x C-e で直前の式の評価, C-c C-r でリージョンの評価ができるつもり。

モード作った

わざわざ M-x からコマンド名を打ち込むのも手間なので,おざなりながらモードを作りました。

最初はマイナーモードとして作っておいて lisp-mode-hook に引っかければいいと思ったんですが,それだと slime とキーバインディングが衝突しました。slime-mode を呼ぶかどうかをファイル名を見て判断するように修正するとか,slime-mode の後で呼ばれるように順序を調整するという手も考えられますが,それもあんまりきれいな解決方法じゃない気がするし,それよりもメジャーモードにしてしまうのが早いだろうということでメジャーモードに。

(defvar mylib-stumpwmrc-mode-map (make-sparse-keymap))
(define-key mylib-stumpwmrc-mode-map "\C-x\C-e" 'mylib-stumpwmrc-eval-last-sexp)
(define-key mylib-stumpwmrc-mode-map "\C-c\C-r" 'mylib-stumpwmrc-eval-region)

(defun mylib-stumpwmrc-mode ()
  (interactive)
  (let ((lisp-mode-hook nil))
    (lisp-mode))
  (use-local-map mylib-stumpwmrc-mode-map))

(add-to-list 'auto-mode-alist '("\\.stumpwmrc$" . mylib-stumpwmrc-mode))

r_takaishir_takaishi2011/05/02 08:51StumpWMでswankサーバを起動して,slimeで接続すればいいと思うのですが,何か問題があるのでしょうか.もしあるなら教えて頂きたいです.

2010-12-16

map vs. loop

| 00:11

xyzzy の session.l の一部を elisp で書いてみたのですが,そのときに感じたこと。どうも複雑な処理は無名関数にして map するより loop で書いたほうが短いし楽だし読みやすい(気がする)。

ということで具体的にコードを並べてみることにしました。

session.l は,開いていたファイルの情報やバッファのポイント位置,ウィンドウの状態などをファイルに書き出しておいて後で復元する機能を提供しています。その中から,現在開いているファイルの情報を集める部分と,それを復元する部分のコードを比べてみます。

まず xyzzy のソースから。

(defun list-buffer-info ()
  (save-excursion
    (let ((info nil))
      (mapc #'(lambda (buffer)
                (when (file-visited-p buffer)
                  (set-buffer buffer)
                  (push (list (get-buffer-file-name buffer)
                              (point)
                              (cons buffer-mode
                                    (mapcan #'(lambda (mode)
                                                (let ((var (and (consp mode) (car mode))))
                                                  (and (symbolp var)
                                                       (boundp var)
                                                       (symbol-value var)
                                                       (list var))))
                                            *minor-mode-alist*))
                              (mapcar #'(lambda (var)
                                          (and (symbolp var)
                                               (local-variable-p var)
                                               (cons var (symbol-value var))))
                                      *buffer-info-variable-list*))
                        info)))
            (buffer-list :buffer-bar-order t))
      (nreverse info))))

(defun restore-buffer-info (info)
  (let ((obuffer (selected-buffer)))
    (mapc #'(lambda (i)
              (let ((file (pop i)))
                (when (file-exist-p file)
                  (handler-case
                      (let ((point (pop i))
                            (mode (pop i))
                            (minor nil))
                        (when (listp mode)
                          (setq minor (cdr mode))
                          (setq mode (car mode)))
                        (let ((*find-file-auto-mode-function* mode))
                          (find-file file))
                        (goto-char point)
                        (mapc #'(lambda (f) (and (fboundp f) (funcall f))) minor)
                        (mapc #'(lambda (x)
                                  (when (and (car x) (symbolp (car x)))
                                    (make-local-variable (car x))
                                    (set (car x) (cdr x))))
                              (pop i)))
                    (file-error (c)
                      (si:*print-condition c))))))
          info)
    (set-buffer obuffer)))

エラーチェックの部分を削ってますが,基本的に同じことをやる elisp のコード。ついでにちょっとコメント入り。

(defun mylib-session-list-buffer-info ()
  (loop for buffer in (buffer-list)
        as file = (buffer-file-name buffer)
        if file collect
        (with-current-buffer buffer
          (list file
                (point)
                ;; collect minor mode info
                (loop for mode in minor-mode-alist
                      as var = (and (consp mode) (car mode))
                      if (and (symbolp var) (boundp var) (symbol-value var))
                      collect var into vars
                      finally (return (cons major-mode vars)))
                ;; collect local variable info
                (loop for var in mylib-session-buffer-info-variable-list
                      if (and (symbolp var) (local-variable-p var))
                      collect (cons var (symbol-value var)))))))

(defun mylib-session-restore-buffer-info (info)
  (save-excursion
    (loop for (file point (mode . minor) local-vars) in info do
          (if (file-exists-p file)
              (ignore-errors
                ;; open visited file,
                ;; (find-file-noselect file)
                (find-file file)   ; 修正 (2010/12/17)
                (when (and (fboundp mode) (not (eq mode major-mode)))
                  (funcall mode))
                ;; move point to the original place,
                (goto-char point)
                ;; restore minor modes,
                (dolist (f minor) (if (fboundp f) (funcall f)))
                ;; and local variables
                (loop for (var value) in local-vars do
                      (make-local-variable var)
                      (set var value)))
            (message "file `%s' does not exist!" file)
            (sit-for 0.5)))))

map 系は無名関数(特に大きめのもの)と一緒に使うとあまり読みやすくない,ということは以前から思っていましたが,それが顕著に表れた気がします。たぶん,第二引数が離れてしまうせいで「何に対して map するのか」がぱっと見てわかりづらいことが,読みにくさを感じさせる一因になっているのではないかと思います。

可読性ともいくらか関係しますが,コードの行数もやや多くなりがちです。lambda で無名関数を書くと,情報量のわりに場所をとる気がします。特に無名関数がよく使われる map の第一引数のようなところでは,lambda と書いてあってもテンプレート通りという感じで,それほど情報量が多くないのではないでしょうか。そういうところに場所をとるものがあると,余計にコードサイズがかさんでいるように感じられるのかもしれません。

それからもう一つありました。map に限らず,高階関数に無名関数を渡すというスタイルは,インデントが深くなりやすいですね。これは可読性にはそれほど関係しないような気もしますが,書いているときにだんだん右に寄ってくるとなんとなく窮屈に感じます。

2010-05-18

anything を触ってみた

| 22:53

初めて自分で anything をカスタマイズしてみました。というか今までほとんど使ってなかったんですけど。

(defvar anything-c-source-shell-command-history
  '((name . "Shell Commands History") (candidates . shell-command-history)
    (action . (lambda (command)
                (let ((name (concat "shell: " command)))
                  (start-process-shell-command name nil command))))))
(add-to-list 'anything-sources
             'anything-c-source-shell-command-history)

anything-sources の documentation と anything-config.el 見ながら適当に書いたら,わりと簡単に動くものができてうれしい。

2009-11-14

内部シンボルの的なものの naming convention

| 21:44

emacsdabbrev 内の内部シンボル的なものには dabbrev--abbrev-at-point のように、package prefix の後ろに '-' が二つ付いてます。

emacs には CL のパッケージのような機構がないのでこういう名前にして「外から触るな」という主張をしているのだと推測しますが、あまりこういうやり方は見たことがありません。

比較的よく見かけるのは CL 風の foo:bar みたいな名前でしょうか。わりと人によって違っているような気がします。そういう場合の名前の付け方は、あまり統一されてないのでしょうか。