Lispy Days

趣味で Lisp な日々 (index) (weblog) (view) (edit) (help)

Emacs Memo

List Comprehension - defmacro によるリストの内包表記 [修正版]

Emacs Lisp版のList Comprehensionの実装についてですが、
list-of-tailの定義の中でlabelsによって定義される局所関数の第2引数
(,var)がEmacs Lispでは動的に束縛されるためにCommon Lisp版の
list-of-tailと動作が異なっていますが、cl.elが定義するlexical-letを使っ
て局所的な束縛に変更すればCLと同等に動作するようにならないでしょうか。

とのツッコミがしかも修正付き。どうもありがとうございます。 lexical-let か〜。うーん Lisp に興味のある人が居るのは嬉しいなぁ。

(defmacro list-of-tail (base expr &rest rest)
  (cond ((null rest) `(cons ,expr ,base))
        ((equal (nth 1 (car rest)) 'in)
         (destructuring-bind (var in generator)
             (car rest)
           (declare (ignore in))
           (let ((f (gensym))
                 (z (gensym)))
             `(labels ((,f (,z ,var)
                           (lexical-let ((,var ,var))
                             (list-of-tail ,z ,expr ,@(cdr rest)))))
                (fold-left #',f ,base ,generator)))))
        (t `(if (funcall (lambda () ,(car rest)))
                (list-of-tail ,base ,expr ,@(cdr rest))
              ,base))))

eshell

eshell の特徴

Welcome to the Emacs shell

~ $ echo hogehoge >#<buffer *scratch*>
~ $ 

スクラッチバッファに echo の出力をリダイレクト(追加のときは >> を使う)

~ $ echo hogehoge >/dev/kill

キルリングに出力

~ $ echo hogehoge >/dev/clip

クリップボードに出力

List Comprehension - defmacro によるリストの内包表記

Common Lisp 用に移植した list-of マクロ が Emacs でも動くことに気がついた. destructuring-bind って elisp でも動くのか…あとはダイナミックスコープに注意すればそれなりに使えるかも.

(defun upto (a b) (if (< b a) nil (cons a (upto (1+ a) b))))
(defun fold-left (f b l)
  (if (null l) b (fold-left f (funcall f b (car l)) (cdr l))))
(defmacro list-of (expr &rest rest)
  `(nreverse (list-of-tail nil ,expr ,@rest)))
(defmacro list-of-tail (base expr &rest rest)
  (cond ((null rest) `(cons ,expr ,base))
    ((equal (nth 1 (car rest)) 'in)
     (destructuring-bind (var in generator)
         (car rest)
       (declare (ignore in))
       (let ((f (gensym))
         (z (gensym)))
         `(labels ((,f (,z ,var) (list-of-tail ,z ,expr ,@(cdr rest))))
           (fold-left #',f ,base ,generator)
         ))))
    (t `(if (funcall (lambda () ,(car rest)))
         (list-of-tail ,base ,expr ,@(cdr rest))
         ,base))))

ただ,upto は elisp だと再帰の深さの限界があるので,↓のように変更する.

(defun upto (from to)
  (let ((acc nil))
    (dotimes (i (1+ (- to from)) (nreverse acc))
      (push (+ i from) acc))))

(list-of (list x y) (x in '(1 2 3)) (y in '(a b c)))
=> ((1 a) (1 b) (1 c) (2 a) (2 b) (2 c) (3 a) (3 b) (3 c))

(list-of (list x y) (x in '(1 2 3)) (y in '(1 2 3)) (= (% (+ x y) 2) 0))
=> ((1 1) (1 3) (2 2) (3 1) (3 3))