talooの日記

2008-07-10

多値

22:18

多値ってものの存在は知っていたけど、「使わなければいいだろ」ってことでほったらかしにしていたらやられた。

というのも、多値の扱い方を知らずに多値を返す関数を使おうとすると、全くもって思ったようにいかない。以下、自分がはまったところと調べた多値の扱い方。


ダメな例
(use rfc.http)
(use htmlprag) ; サードパーティ製ライブラリ

(define (f host path)
  (http-get host path))

(html->sxml (f "www.example.com" "/"))

これがダメな理由は、http-get が多値を返すから。多値を返す関数戻り値をそのまま使おうとすると、多値の先頭の値だけが適用されてしまう。http-get のリファレンスでは以下のように書かれている。

Gauche Users’ Reference: Top

戻り値: 全ての手続きは3つの値を返します。

1つ目は、RFC2616で定義されているステータスコード文字列値(例えば、成功時の 200、"Not found"の404など)です。

2つ目は、パーズされたヘッダのリストで、リストの要素は(header-name value …)です。header-nameはヘッダの文字列名(例えば、 "content-type"や"location"など)で、valueは対応する値の文字列値です。 ヘッダ名は小文字に変換されます。値は、RFC2822で定義されている無指定行区切 (ソフト・ライン・ブレイク)が除かれる以外はそのままです。 サーバが同じ名前のヘッダを1つ以上返した場合は、 1つのリストに統合されます。それ以外では、2つ目の戻り値に おけるヘッダのリストの順番は、サーバの応答での順番と同じです。

3つ目の戻り値は、サーバの応答におけるメッセージボディです。 デフォルトでは、文字列で表現されたメッセージボディそのものです。 サーバの応答がボディを持たない場合、3つ目の戻り値は#fです。 キーワード引数によって、メッセージボディがどのように扱われるかを制御できます。 例えば、中間的な文字列を作らずに、返されたメッセージボディを直接ファイルに 格納することが出来ます。詳細は以下で説明しています。

で、多値をいい感じにうけとってごにょごにょやるにはどうしたらいいのか。またリファレンスの引用。

Gauche Users’ Reference: Top

Special Form: receive formals expression body …

[SRFI-8] この構文により、多値を受け取ることができます。 formalsはシンボルのリストです。不完全なリストであっても構いません。 expressionが評価され、返された値がlambda形式の引数の束縛と 同じようにしてformals内の変数と束縛され、その環境下でbody …が 評価されます。

    (define (divrem n m)
      (values (quotient n m) (remainder n m)))

    (receive (q r) (divrem 13 4) (list q r))
       (3 1)

    (receive all (divrem 13 4) all)
       (3 1)

    (receive (q . rest) (divrem 13 4) (list q rest))
       (3 (1))

とまぁ、receive ってやつを使えばいい模様。で、最初のダメな例は最終的に以下のようになった。

(use rfc.http)
(use htmlprag)

(define (f host path)
  (receive (code header src) (http-get host path) src))

(html->sxml (f "www.example.com" "/"))

多値の存在意義なんかも、ググると出てくる。リストでいいじゃん、と思ったけど、まぁなんか色々あるらしい。

2008-07-03

はてな記法

01:38

pre記法内で | を使うにはどうしたらいいんだ。

ってあれ

なんかちゃんと表示されてるし。勘違いか。ていうか手軽に日記消すことができないのかなぁ。

マクロとか

01:29

発売当初に買って積んでいた gauche 本を今更読んでいる。昨日マクロの章を読み、なんとなくマクロってどんなものかということが掴めた。

syntax-rules と define-macro が受けとるもの

syntax-rules のほうは「受けとる」とは若干違うと思うけど。

これらが受けとるのは S 式そのものであるということが肝。普通の関数で受けとるものは、式を評価したものでしかない。式そのものを受けとるには、quote するとかしかないんじゃないかなぁ。評価させたくないだけであれば lambda でくるむとか。

で、syntax-rules やら define-macro なんかは式を直接受けとることができるから、普通の手続きとは違う動きを定義することができる。これが構文を拡張する機構だと言われる所以?

ユニットテスト用のマクロ

ユニットテストでは大体「ある手続きの結果が予測値と一致することをテストする」という手続きをとる。関数型プログラミングでは「ある手続き」は大体式になる。マクロを使えば式そのものを渡すことができ、評価するタイミングをモジュール側で好きなようにできる。ユーザとしても、lambda でくるむ必要がなくなるという利点がある。以下は今朝電車の中で書いたもの。

(define-syntax test
  (syntax-rules ()
    ((_ name expect expr verify)
     (begin (display name)
            (let ((r expr))
              (if (verify expect r)
                (print " =>  ok")
                (print #`" => expect ,|expect| but got ,|r|")))))
     ((_ name expect expr)
      (test name expect expr equal?))))

細かいところは全然作ってないんだけど。

昨日今日で知った人でもなんとなくできてしまうところが、scheme の面白いところだよなぁと思う今日このごろ。