Gauche:クロージャの中身
kouさんの:
(lambda (x y) (+ x y)) の評価結果が #<closure 0x81b0dc0(x y) (+ x y)> ってなったらいいのになぁ.
から派生した話題。
クロージャの中身を取り出せたら?
個人的には (define f (lambda (x y) (+ x y)) とした f について (closure->lambda f) => (lambda (x y) (+ x y)) って感じで普通にリストとして取り出せると色々加工の仕方もありそうな気がするなぁ。cut-sea:2003/06/28 10:01:45 PDT
- コンパイル前の式を保存しておくことはそんなに難しくはないと 思いますが、一般的な状況では式が大きくなるので#<closure...>中に 表示するのは難がありますね。
- ところでclosure->lambdaがあったとして、どんなふうに使えるでしょう? デバッグ情報にはなりそうですが。クロージャのセマンティクスは 表式+環境なので、表式だけ取り出しても元のクロージャの意味は復元できません Shiro (2003/06/28 13:30:57 PDT)
復元が目的ではないですね。(いやどうなんだろう。ある意味復元だろうか) すぐ具体的にってのは出て来ないんですけど、 f と g という手続きを定義して、 それらの手続きを組み合わせることで亜種の手続きなんかを作りたいとします。 その場合、 f と g の手続きを原子としたら、 普通その化合物的なものしか作れないですよね。
そうではなく、 f と g の各部分式を組み合わせて新種の元素を作る みたいなことが出来るようにならないかなと思います(錬金術)。 冗長だけど、例えばカレーを作る手続き c とパンを作る手続き p がそれぞれのレシピを記述している とすると、それらのクロージャ同士をもらってもカレーパンをつくるレシピは作れない。 なぜならパンを作るレシピの中にカレーを作るレシピを組み込むあるいは 巧みに混ぜ合わせて、パン生地をこねてから焼成する前にカレーを包む必要があるから。 つまりそれぞれからカレーパンという亜種が発明されることは無い(と言い切っていいのかな?)
直接自分で書くのではなくてそういった亜種の生成をランダムにもしくは システマティックに行う手続きを用意して生み出させて遊べないかな〜 とか思ったことがあります。
もちろん意味の無いものも多数作成されるわけですが。 だいぶ以前漠然と考えたのは既にある整列のアルゴリズムを何種類か定義して、 それらをざくざく上記の方法で組み合わせさせて、生成物にテストを実施させて 整列の機能が継承された亜種の整列アルゴリズムなんかを生み出せないかな〜とか。 ただ、scheme の実装者からすると C で実装されたプリミティブはどうするねんって感じですから美しくはないかな。
ちなみにあの後 define そのものを define-syntax するなどしてそれっぽく出来そうな感じも得られたので今度気が向いたら真面目に define-syntax 組んでみようかなと。 まだいまだに十分使いこなせてないので勉強が先ですけど。。。cut-sea:2003/06/28 14:01:49 PDT
- 上記の様なことをしたくば、とりあえず (define f '(lambda (x y) (+ x y))) みたいにして、扱ってやりゃ済む話なのかな。(自己完結だ^^;)cut-sea:2003/06/28 14:25:02 PDT
Shiro (2003/06/28 14:44:02 PDT): そういうことなら、クロージャのセマンティクスには 手をつけずに、独自の言語拡張を組むのが良いような気がします。例えば こんなふうにしておくと:
(define-class <klosure> () ((program :init-keyword :program) (closure :init-keyword :closure))) (define-syntax define-klosure (syntax-rules () ((_ (var . args) body ...) (define var (make <klosure> :program '(lambda args body ...) :closure (lambda args body ...)))) ((_ var expr) (define var (make <klosure> :program 'expr :closure expr))) )) (define-method object-apply ((k <klosure>) . args) (apply (ref k 'closure) args)) (define-method klosure->lambda ((k <klosure>)) (ref k 'program))
こんなふうになります:
gosh> (define-klosure (f x y) (+ x y)) f gosh> f #<<klosure> 0x82a1290> gosh> (f 1 2) 3 gosh> (klosure->lambda f) (lambda (x y) (+ x y))
- あれ?ひとつ疑問が。。。 make ... の返り値は klosure クラスのインスタンスを返すんですよね? 単純に (f 1 2) を評価した時に f が :closure の値として作られたクロージャであることはどこで決まるのでしょうか? この辺りのドキュメントってあります? cut-sea:2003/06/28 20:17:20 PDT
- 秘密はメソッドobject-applyにあります。
リファレンスマニュアルのGaucheRefj:適用可能なオブジェクトを
見て下さい。Shiro
- ずっとリファレンスマニュアルのオブジェクトシステムを見てた。。。 正規表現の扱いがなんとなく気になってたんだけど、これがそうだったのか。cut-sea
gosh> (object-apply f 3 4) 7
実はOOの部分って今まで見てみぬ振りをして来たんですけど、こうしてみると十分分かりやすい構造ですね。(正直悔しくて今苦笑い中2003/06/28 15:14:33 PDT) ところで、これ r5rs そのものには含まれないですよね。 OO が無い処理系でやるとしたら define-class や define-method した部分を 別途定義してやればいいんですよね。 program と closure のリストでもたせておいて、 各メソッドはそれぞれ car/cadr にアクセスする様にしてやると。 define-syntax 中の make <klosure>...の部分は、、、単にそれぞれを cons してやればいいか。 でもやっぱりちゃんと #<<klosure> 0x812b310>とかって返って来るとなかなかいいですね。なんとなく安心できるというか。cut-sea
クロージャの中身を見る方法
Shiro: ちなみに、アンドキュメンテッドな機能ですが、クロージャの中身を ディスアセンブルして調べることはできます。 (アンドキュメンテッドなのは、今後仕様が変わるかもしれないからです)。
gosh> (use gauche.vm.disasm) (#<module gauche.vm.disasm> #<module gauche.uvector> #<module gauche.interactive>) gosh> (define a (lambda (x y) (+ x y))) a gosh> (disasm a) LREF1-PUSH ;; x LREF0 ;; y NUMADD2 ;; (+ x y) RET ()