趣味プログラマ。 気の向くままに思い付きのままにコードをいじって遊んでます。
私の今迄の疑問。
そして要望。(解決済み)
数値文字参照の扱いが htmlprag と ssax で違ってた。
htmlprag ではこんな感じ。
(html->sxml "<html><body><div>㊌</div></body></html>") => (*TOP* (html (body (div (& 12940)))))
ssax ではこんな感じ。
(ssax:xml->sxml (open-input-string "<html><body><div>㊌</div></body></html>") '()) => (*TOP* (html (body (div "㊌")))) ;; gauche-charactor-encoding が utf-8 以外のときはどうなるんだろう?
htmlprag で作った sxml を sxml:sxml->xml で変換しようとするとエラーになってしまう。 shtml->html を使えばいいだけの話ではあるけど、同じ sxml 関連だからと思ってこういう風に出自の違うライブラリを混ぜて使ってると予想外のところで躓いてしまうこともあるという教訓。
(2011/02/11 07:39:49 PST)
http://practical-scheme.net/chaton/gauche/a/2011/02/10#entry-4d542b4d-9493d
齊藤 2011/02/10 10:15:41 PST ところで話は変わりますが、 output string port に蓄積したものを今度は input string port として読み出したいってケースは珍しくないと思うんですが、 get-output-string で取り出して open-input-string するしか無いですか? なんとなく非効率な気がするんですが、内部的には共有されてたりするのかな。
shiro 2011/02/10 10:53:36 PST ああ、get-output-stringはコピーが発生しちゃいますね。output string portは最終的な長さがわからないのでチャンクのリストの形でデータを保持してるので。今のところその蓄積されたデータを得る方法はget-output-stringだけです。
procedural buffered portを使ったらパイプのような対のポートを作れるんじゃないかな。
齊藤 2011/02/10 11:15:24 PST <buffered-output-port> を使えば案外簡単に出来そうな予感。
と言うわけでやってみた。
まずは出力ポートから。
(use gauche.vport)
(use gauche.uvector)
(define-class <ostring> (<buffered-output-port>)
((chunks :init-keyword :chunks)
(last-chunk :init-keyword :last-chunk)))
(define-method initialize ((obj <ostring>) initargs)
(let1 chunks (cons #f '())
(next-method obj
(list*
:chunks chunks
:last-chunk chunks
:flush (lambda (buffer flag)
(let1 lc (~ obj 'last-chunk)
(set-cdr! lc (cons (u8vector-copy buffer) '()))
(set! (~ obj 'last-chunk) (cdr lc))
(u8vector-length buffer)))
initargs
))))
(define (make-open-ostring . opt) (apply make <ostring> opt))
試しに動作させてみると…
(define a (make-open-ostring :buffer-size 2)) ;; ←効果を見易くするためバッファを小さく (write '(hoge huga hige) a) (flush a)
このとき a に蓄積されているデータの様子は
(~ a 'chunks) ;; => (#f #u8(40 104) #u8(111 103) #u8(101 32) #u8(104 117) #u8(103 97) #u8(32 104) #u8(105 103) #u8(101 41))
入力ポートを作るコードはこんな感じ。
(define-class <istring> (<buffered-input-port>)
((chunks :init-keyword :chunks)))
(define-method get-input-port ((obj <ostring>))
(let1 x (make <istring> :chunks (cdr (~ obj 'chunks)))
(set! (~ obj 'chunks) (cons #f '()))
x))
(define-method initialize ((obj <istring>) initargs)
(next-method obj
(list* :fill
(lambda(buffer)
(let* ((obl (u8vector-length buffer))
(chunks (~ obj 'chunks))
(ib (if (null? chunks) #f (car chunks))))
(if (not ib)
0
(let ((ibl (u8vector-length ib)))
(cond ((= ibl obl)
(u8vector-copy! buffer ib)
(set! (~ obj 'chunks) (cdr chunks))
ibl)
((< ibl obl)
(u8vector-copy! buffer ib)
(set! (~ obj 'chunks) (cdr chunks))
ibl)
((> ibl obl)
(u8vector-copy! buffer ib)
(set-car! (chunks (u8vector-copy ib obl)))
obl))))))
initargs
)))
先程の出力ポートから入力ポートを作って入力してみる。
(define b (get-input-port a)) (read b) ;; => (hoge huga hige)
入力ポート内での状態管理が手抜きなので無駄にコピーしちゃってるけど、とりあえずは機能してるみたいなので良しとしよう。
(2011/02/10 13:12:25 PST)
curl バインディングを使ってみた。
<curl> は url というスロットがあり、そのアクセサは url-of なのだけれど、 url-of は export されていないせいか curl モジュールを use しただけでは見えない。 (define-generic url-of) (use curl) としてみても url-of ⇒ #<generic url-of (0)> だった。
curl モジュールの中では initialize メソッドを定義しているようで、これも export はされていないのに curl モジュールを use しただけでちゃんと機能しているようだ。 initialize は特別扱いなんだろうか。
メソッドとモジュールの関係がよくわからない感じ。
(2010/01/23 08:48:23 PST)
CommonLisp には gensym があって、gensym は Gauche でも使える。 r6rs の範囲内で gensym を書くことは出来るだろうかということを考えた。
本来の gensym は intern されないシンボルを作るものだ。 さすがに言語組込の拡張機能として用意しなければ書けないだろうとは思ったのだが、色々と考えている内にこんなものが出来上がった。
(define (my-gensym)
(car (syntax->datum
((lambda(x)
(syntax-case x ()
((_ a)
(generate-temporaries (syntax (a))))))
#'(_ _)))))
これが gensym として使えるとは思ってない。 しかし、遊んでいる内に気付いたことがある。
(define g (my-gensym)) (define h (string->symbol (symbol->string g))) (write (eq? g h))
r6rs 的にはスペルが同じシンボルを比較すれば eq? になるはず。 なので、ここでは #t が返るのが正当だと私は思う。 実際、Mosh, plt-scheme, petite-chez-scheme ではそうなる。 でも、 Ypsilon では #f だった。
(2009/12/08 03:10:48 PST)
(defn my-gensym [] (first `(sym#))) (let [s (my-gensym)] (= s (symbol (str s)))) ;; => trueと思ったら標準の gensym がそもそも intern しているという(core.clj)。
(let [s (gensym)] (= s (symbol (str s)))) ;; => true
schemeのマクロは難しい。datum->syntaxの第一引数の意味がまだ良くわからない。文脈情報ってなんやねんという感じ。今は名前解決の起点と理解しているけれど、これでいいのだろうか。 (2008/04/08 07:07:16 PDT)
(define-syntax s1
(lambda (form)
(syntax-case form ()
((ctx)
#`(let* ((x 3)
(y #,(datum->syntax #'ctx 'x)))
(* y y))))))
(define-syntax s2
(lambda (form)
(syntax-case form ()
((ctx)
#`(let* ((x 5)
(y #,(datum->syntax #'k 'x)))
(* y y))))))
(display (let ((x 4)) (s1))) ; =| 16
(newline)
(display (let ((x 4)) (s2))) ; =| 25
(newline)
s1 の方は ctx = s1 の導入されたスコープ(= s1 の静的スコープ)の x を参照するようになるので、ここの x は (display (let ((x 4)) (s1))) の方の x で x = 4 になります。一方、s2 の方では syntax-case 内で新たに導入された識別子 k と同じスコープの x、つまり y の直前に let* で束縛された x になるので (let* ((x 5) ...) ...) の x で x = 5 になり、各 display はそれぞれ 16 と 25 を表示します。たぶん、 R6RS の説明が一番簡潔でわかりやすいんじゃないかと思います。 datum->syntax の説明の最後には syntax-case で簡易 define-macro を書く、みたいな例もあります。
(define-syntax expand/scope
(lambda(x)
(syntax-case x ()
((k scope body)
(with-syntax
((p (datum->syntax #'scope
(syntax->datum #'body))))
#'p)))))
(let foo ((a 3))
(let bar ((a 2))
(let ((a 1))
(expand/scope bar a))))
letで導入したbarと同じスコープでaが解釈されて3になることを期待したのですが、結果は1となります。scopeにマッチしたbarはletで導入したbarでなくexpand/scopeが起動したときに導入されたことになるのでしょうか。このへんの理屈がわかっていません。
(let foo ((a 3))
(let bar ((a 2))
(let-syntax ((expand/bar-scope
(lambda(x)
(syntax-case x ()
((k body)
(with-syntax
((p (datum->syntax #'bar
(syntax->datum #'body))))
#'p))))))
(let ((a 1))
(expand/bar-scope a)))))
この結果からすると「bar の導入された『文脈』において、 expand/scope の a の属する静的スコープに従って a を評価する」というわけでもないようです。先の例ではscopeにマッチしたbarがある場所でaが解釈されて、barが導入された場所とは解釈されないのではないか、なんて思ったりしているのですが、なんだか根本的な勘違いがある気もしてどうにもスッキリしないですね。
syntax-caseマクロはハイジニックなマクロもそうでないマクロも書けるということで、そんならsyntax-caseでdefine-macroを模倣できるんではないかとふと意味も無く思いついて書いてみた。
(define-syntax define-macro
(lambda(x)
(syntax-case x ()
((_ name body)
(with-syntax ((syntax-body
(datum->syntax-object
(syntax k)
(syntax-object->datum (syntax body)))))
(syntax
(define-syntax name
(lambda(y)
(syntax-case y ()
((name a (... ...))
(let* ((m (syntax-object->datum (syntax (a (... ...)))))
(n (eval `(syntax-body ,@m) (interaction-environment))))
(with-syntax ((r (datum->syntax-object (syntax k) n)))
(syntax r))))))
)))))))
なにやらあまりにもごちゃっとしているし、途中でevalしてるどころか(r6rsでは廃止されたはずの)interaction-environmentを使ってるしで無様なものになってしまった。既存の似たようなことをしたコードが無いものかと"(define-syntax (define-macro"を検索ワードとしてぐぐってみると、まさに同じことをやろうとしているコードが見つかった。
http://c2.com/cgi/wiki?DefineSyntax
こういう書き方もあるんだなぁ。 datum->syntax-objectの第一引数の意味をさっぱり理解できてなくてSchemeを使用したメタプログラミングの記事にある「ちょっとした魔法の式です」をとりあえず鵜呑みにして(syntax k)と書いているので、まじめに理解に努める必要アリと思われる。(2008/01/09 07:13:40 PST)
(define-macro foo (lambda () '(list x))) (let ((x 10)) (foo))はエラーになりますが、最後の(syntax k)を(syntax name)に書き換えると(10)に評価されます。
閉じ括弧をコッカと呼ぶのがアリならXMLの閉じタグはグタと読んでもよさそうなもんだ。
(2006/11/02 21:57:20 PST)