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 |

2009-11-22

KMRCLを眺める (19) with-each-file-line

| 19:26 | KMRCLを眺める (19) with-each-file-line - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める (19) with-each-file-line - わだばLisperになる

今日は、KMRCLのmacros.lispの中からWITH-EACH-FILE-LINEです。

定義は、

(defmacro with-each-file-line ((var file) &body body)
  (let ((stream (gensym)))
    `(with-open-file (,stream ,file :direction :input)
      (with-each-stream-line (,var ,stream)
        ,@body))))

というもので、前回のWITH-EACH-STREAM-LINEにファイルを開く指定を被せたものです。

使用例は、

(WITH-OUTPUT-TO-STRING (OUT)
  (LET ((LINE-NUM 0))
    (WITH-EACH-FILE-LINE (LINE "/etc/motd")
      (FORMAT OUT "~4D: ~A~%" (INCF LINE-NUM) LINE))))
;⇒
"   1: Linux setq 2.6.31-14-generic #48-Ubuntu SMP Fri Oct 16 14:05:01 UTC 2009 x86_64
   2: 
   3: To access official Ubuntu documentation, please visit:
   4: http://help.ubuntu.com/
"

みたいな感じでしょうか。

実は自分は、最初に

(WITH-EACH-FILE-LINE (LINE "/etc/motd")
  (WITH-OUTPUT-TO-STRING (OUT)
    (LET ((LINE-NUM 0))
      (FORMAT OUT "~4D: ~A~%" (INCF LINE-NUM) LINE))))
;⇒ NIL

と書いて若干はまりました。

原因はマクロ展開するとすぐに分かるのですが、

(WITH-OPEN-FILE (#:G2727 "/etc/motd" :DIRECTION :INPUT)
  (LET ((#:G2730 #:G2727) (#:G2728 '#:G2729))
    (DO ((LINE (READ-LINE #:G2730 NIL #:G2728) (READ-LINE #:G2730 NIL #:G2728)))
        ((EQL LINE #:G2728))
      (WITH-OUTPUT-TO-STRING (OUT)
        (LET ((LINE-NUM 0))
          (FORMAT OUT "~4D: ~A~%" (INCF LINE-NUM) LINE))))))

となるのでこのマクロでは入れ子の順番が大事ということになります。

どうもWITH-というマクロで入れ子の順番を気にしないといけないというマクロはあまりないので、WITH-EACH-FILE-LINEという名前よりは、DO-FILE-LINESとかの方が良いんじゃないかと思いました。

(WITH-OUTPUT-TO-STRING (OUT)
  (LET ((LINE-NUM 0))
    (DO-FILE-LINES (LINE "/etc/motd")
      (FORMAT OUT "~4D: ~A~%" (INCF LINE-NUM) LINE))))

という方が明確な気がしますし、

(DO-FILE-LINES (LINE "/etc/motd")
  (WITH-OUTPUT-TO-STRING (OUT)
    (LET ((LINE-NUM 0))
      (FORMAT OUT "~4D: ~A~%" (INCF LINE-NUM) LINE))))

これはぱっと見ただけで何か勘違いがあるんじゃないかと、なんとなく分かります。(WITH-EACH-を見て勘がすぐに働けば問題ないのですが…)

それとWITH-OPEN-FILEに渡すキーが:DIRECTIONだけなのも使い勝手が良くなかったので拡張してみました。

(DEFMACRO DO-FILE-LINES ((VAR FILE &REST KEYS &KEY &ALLOW-OTHER-KEYS) &BODY BODY)
  (LET ((STREAM (GENSYM))
        (KEYS (COPY-LIST KEYS)))
    (REMF KEYS :DIRECTION)
    `(WITH-OPEN-FILE (,STREAM ,FILE :DIRECTION :INPUT ,@KEYS)
       (WITH-EACH-STREAM-LINE (,VAR ,STREAM)
         ,@BODY))))

こういう場合のKEYの書き方の定番ってどういうのが良いのかいまいち分かりませんが、とりあえずで書いています。(横着しないでDO-FILE-LINESの仮引数のところで丁寧に全部書くべきなのかも)

ゲスト



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