Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2007-11-21


Luaのgeneric forの真似

| 12:57 | Luaのgeneric forの真似 - わだばLisperになる を含むブックマーク はてなブックマーク - Luaのgeneric forの真似 - わだばLisperになる

[Think IT] 第2回:言語開発者が目標にするパフォーマンス「Lua」 (1/3)の記事で、Luaのgeneric forという機構では、ループに関数を返す関数を取ることができて(これをLuaではイテレータというとのこと)非常に柔軟であるらしい、というところを見て、何か凄く便利っぽい!と思ったので真似して作ってみることにしました。

とりあえず、ここで例として取り上げられている、uptoは、

(defun upto (max)
  (let ((count 0))
    (lambda ()
      (if (< count max)
	  (incf count)
	  (values nil t)))))

のように書けるかと思います。

空になったらnilを返して終了というのでも良いと思うんですが、それだと単体のnilを返した時も終了となってしまうので、空になったら多値を返し、2値目でnil以外の値を返してみることにしました。

それで、これを呼び出すループの機構を考えるわけですが、doが好みなので、

(dog ((l (cdrdown '(foo bar baz zot)))
      (i (upto 10)))
  (print (list (car l) i)))

;=>
;  (FOO 1) 
;  (BAR 2) 
;  (BAZ 3) 
;  (ZOT 4) 

のように決めて、適当にマクロででっち上げました。

(defmacro dog ((&rest binds) &body body)
  (let ((vars (mapcar #'car binds))
	(vals (mapcar #'cadr binds))
	(iters (mapcar (lambda (x) (declare (ignore x)) (gensym "ITER-")) binds))
	(endp (gensym "ENDP-"))
	(end (gensym))
	(val (gensym)))
    `(let ,(mapcar #'list iters vals)
       (let (,endp)
	 (do ,(mapcar (lambda (v i)
			`(,v
			  (multiple-value-bind (,val ,end) (funcall ,i)
			    (unless ,endp (setq ,endp ,end))
			    ,val)
			  (multiple-value-bind (,val ,end) (funcall ,i)
			    (unless ,endp (setq ,endp ,end))
			    ,val)))
		      vars iters)
	     (,endp)
	   ,@body)))))

終了判定の多値の受けとりで上手い書き方が思い付かず、存外ぐちゃぐちゃになりました。

それで、どっちかというと肝は関数を返す関数を工夫して書けば応用が広がるところだと思うんですが、

; (1 2 3)

; (2 3)

; (3)

のような事をする関数を返す関数は、

(defun cdrdown (list)
  (let ((l list))
    (lambda () 
      (if l
	  (prog1 l (pop l))
	  (values nil t)))))

; (1 2 3 4)

; 1

; 2

; 3

; 4

; nil,t

と順番に値を返す関数を返す関数は

(defun next-item (list)
  (let ((l list))
    (lambda () 
      (if l
	  (prog1 (car l) (pop l))
	  (values nil t)))))

という風に簡潔にどんどん拡張できそうです。

と、色々考えてみて思ったのですが、関数を返す関数を取るループではないものの、これはSERIESの使い勝手と非常に近いということで、SERIESだと

(iterate ((l (scan-sublists '(foo bar baz zot)))
	  (i (scan-range :from 1)))
  (print (list (car l) i)))

という風に書けると思います。

SERIESのscan系の関数を自分でどんどん拡張して書けば、同じく柔軟に使えそうです。

ゲスト



トラックバック - http://cadr.g.hatena.ne.jp/g000001/20071121