yamasushi(2013/04/19 09:30:27 UTC)万能アクセサを使う小物をまとめてみます。
<dictionary>のときはdict-get,<procedure>なら関数適用,<module>ならglobal-variable-ref。
(define-class <%ref-adapter%> [<instance-pool-mixin>]
[ (proxy :init-keyword :proxy :init-value #f )
(getter :init-keyword :getter :init-value #f )
(setter :init-keyword :setter :init-value #f )
] )
(define-method %pool-make (proxy :optional (getter #f) (setter #f))
(if-let1 obj (instance-pool-find <%ref-adapter%>
(every-pred
($ eq? proxy $ flip slot-ref 'proxy $)
($ eq? getter $ flip slot-ref 'getter $)
($ eq? setter $ flip slot-ref 'setter $) ) )
obj
(make <%ref-adapter%> :proxy proxy :getter getter :setter setter) ) )
; compare
(define-method object-equal? ( (x <%ref-adapter%>) (y <%ref-adapter%>) )
(equal? (slot-ref* x'proxy x'getter x'setter) (slot-ref* y'proxy y'getter y'setter) ) )
; get
(define-method ref ((self <%ref-adapter%>) key . rest)
(let* [ [proxy (slot-ref self 'proxy )]
[getter (slot-ref self 'getter )] ]
(cond
[(and proxy getter)
(if (null? rest)
(getter proxy key)
(apply getter proxy key rest) ) ]
[proxy
(if (null? rest)
(proxy key)
(apply proxy key rest) ) ]
[getter
(if (null? rest)
(getter key)
(apply getter key rest) ) ]
[else (errorf "cannot ref self:~a key:~a rest:~a" self key rest) ] ) ) )
; set
(define-method (setter ref) ((self <%ref-adapter%>) key value . rest)
(let* [ [proxy (slot-ref self 'proxy )]
[setter (slot-ref self 'setter )] ]
(cond
[ (and proxy setter)
(if (null? rest)
(setter proxy key value)
(apply setter proxy key value rest) ) ]
[proxy
(if (null? rest)
(proxy key value)
(apply proxy key value rest) ) ]
[setter
(if (null? rest)
(setter key value)
(apply setter key value rest) ) ]
[else (errorf "cannot set self:~a key:~a value:~a rest:~a" self key value rest) ] ) ) )
(define-method write-object ((self <%ref-adapter%>) port)
($ format port "<%ref-adatper% proxy:~a getter:~a setter:~a>" $* slot-ref* self'proxy self'getter self'setter ) )
; 空気を読んでアクセッサ付きにする
(define-class <x->~> [<singleton-mixin>] [])
(define x->~ (instance-of <x->~>) )
(define-method object-apply ((self <x->~>) (proc <procedure>))
(%pool-make proc) )
(define-method object-apply ((self <x->~>) (getter <procedure>) (setter <procedure>))
(%pool-make #f getter setter ) )
(define-method object-apply ((self <x->~>) (dict <dictionary>))
(%pool-make dict dict-get dict-put!) )
(define-method object-apply ((self <x->~>) (module <module>))
(%pool-make module global-variable-ref) )
(define-method ref ( (x <procedure>) (self <x->~>) ) (self x) )
(define-method ref ( (x <dictionary>) (self <x->~>) ) (self x) )
(define-method ref ( (x <module>) (self <x->~>) ) (self x) )
gosh> (~ gauche-info-root "*source*" "*module*" "gauche.generator")
*** ERROR: no applicable method for #<generic ref (19)> with arguments (#<<gauche-module-finder> 0x8bfac18> "gauche.generator")
Stack Trace:
_______________________________________
0 (eval expr env)
At line 173 of "/usr/local/share/gauche-0.9/0.9.4_pre3/lib/gauche/interactive.scm"
gosh> (~ gauche-info-root "*source*" "*module*" x->~ "gauche.generator")
("/usr/local/share/gauche-0.9/0.9.4_pre3/lib/gauche/generator.scm")
gosh> (~ (find-module 'gauche) 'cond-list)
*** ERROR: object of class #<class <module>> doesn't have such slot: cond-list
Stack Trace:
_______________________________________
0 (eval expr env)
At line 173 of "/usr/local/share/gauche-0.9/0.9.4_pre3/lib/gauche/interactive.scm"
gosh> (~ (find-module 'gauche) x->~ 'cond-list)
#<macro cond-list>
; global accessor
(define-class <module-proxy> [<singleton-mixin>] [])
(define module-proxy (instance-of <module-proxy>) )
(define-method ref ((self <module-proxy>) (name <symbol>))
(find-module name) )
(define-method ref ((self <module-proxy>) (path <string>))
(find-module (path->module-name path) ) )
(define-class <system-proxy> [<singleton-mixin>] [])
(define system-proxy (instance-of <system-proxy>) )
(define-method ref ((self <system-proxy>) (gtag <keyword> ) )
(case gtag
[(:uname ) (sys-uname)]
[(:hostname ) (sys-gethostname) ]
[(:domainname) (sys-getdomainname) ]
[(:cwd ) (sys-getcwd ) ]
[(:gid ) (sys-getgid ) ]
[(:egid ) (sys-getegid ) ]
[(:uid ) (sys-getuid ) ]
[(:euid ) (sys-geteuid ) ]
[(:groups ) (sys-getgroups) ]
[(:login ) (sys-getlogin) ]
[(:pgrp ) (sys-getpgrp) ]
[(:pid ) (sys-getpid) ]
[(:ppid ) (sys-getppid) ]
[(:times ) (sys-times) ]
[(:ctermid ) (sys-ctermid) ]
;;
[(:strerror ) (x->~ sys-strerror) ]
;;
[(:pgid ) (x->~ sys-getpgid sys-setpgid ) ]
[(:rlimit ) (x->~ sys-getrlimit sys-setrlimit ) ]
[(:env ) (x->~ sys-getenv sys-setenv ) ]
;;
[else (errorf "unknown tag ~a" else)]
) )
(define-method (setter ref) ((self <system-proxy>) (gtag <keyword>) value)
(case gtag
[(:gid ) (sys-setgid value) ]
[(:uid ) (sys-setuid value) ]
[ else (errorf "unknown tag ~a for setter" else)]
) )
gosh> (~ module-proxy 'gauche x->~ 'cond-list) #<macro cond-list> gosh> (~ system-proxy :env "PATH") "/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/shuji/gauche/command" gosh> (~ system-proxy :env "TMP") #f gosh> (set! (~ system-proxy :env "TMP") "hoge") #<undef> gosh> (~ system-proxy :env "TMP") "hoge"
; 万能アクセサ的なslot-ref
; ~を改造
(define %slot-ref*
(getter-with-setter
(case-lambda
[(obj selector) (slot-ref obj selector)]
[(obj selector . more) (apply slot-ref* (slot-ref obj selector) more)])
(case-lambda
[(obj selector val) ((setter slot-ref) obj selector val)]
[(obj selector selector2 . rest)
(apply (setter slot-ref*) (slot-ref obj selector) selector2 rest)])))
(define-method slot-ref* (obj (sym <symbol>) . syms )
(apply %slot-ref* obj sym syms) )
(~~ obj 'x) = (~ obj 'x) (~~ obj 'x 'y) = (~ obj 'x 'y) (~~ obj1 obj2 'x 'y obj3 'u 'v 'w ) == (list (~~ obj1 obj2 'x 'y ) (~~ obj3 'u 'v 'w ) ) == (list (list (~ obj1 'x 'y) (list obj2 'x 'y) ) (~ obj3 'u 'v 'w) )
(define (%ref% refn pred . arg)
($ (^x (if (= (length x) 1) (car x) x ) )
$ map
(match-lambda
[( ((? (complement pred) obj) ) ( (? pred syms) ... ) )
(apply refn obj syms) ]
[( ((? (complement pred) objs) ... ) ( (? pred syms) ... ) )
(map (cut apply refn <> syms) objs) ] )
$ flip slices 2 $ group-sequence arg :key pred)
)
(define (%flip-ref% refn pred . arg)
($ (^x (if (= (length x) 1) (car x) x ) )
$ map
(match-lambda
[( ( (? pred syms) ... ) ((? (complement pred) obj) ) )
(apply refn obj syms) ]
[( ( (? pred syms) ... ) ((? (complement pred) objs) ... ) )
(map (cut apply refn <> syms) objs) ] )
$ flip slices 2 $ group-sequence arg :key pred)
)
; (slot-ref* obj1 obj2 'a 'b obj3 'x)
; == (list
; (list (slot-ref* obj1 'a 'b) (slot-ref* 'a 'b))
; (slot-ref* obj3 'x) )
(define (slot-ref* . arg) (apply %ref% %slot-ref* symbol? arg ) )
(define (flip-slot-ref* . arg) (apply %flip-ref% %slot-ref* symbol? arg ) )
(define (~~ . arg) (apply %ref% ~ symbol? arg ) )
(define (flip~~ . arg) (apply %flip-ref% ~ symbol? arg ) )
gosh> (~~ (find-module 'gauche) 'table) #<hash-table eq? 0x8204ed8> gosh> (~~ (find-module 'gauche) 'table 'cond-list) #<gloc gauche#cond-list> gosh> (~~ (find-module 'util.list) 'table) #<hash-table eq? 0x8398208> gosh> (~~ (find-module 'util.list) 'table 'cond-list) #<gloc util.list#cond-list> gosh> (~~ (find-module 'gauche) 'table (find-module 'gauche.generator) 'table) (#<hash-table eq? 0x8204ed8> #<hash-table eq? 0x82bd3c0>) gosh> (~~ (find-module 'gauche) (find-module 'gauche.generator) 'table) (#<hash-table eq? 0x8204ed8> #<hash-table eq? 0x82bd3c0>) gosh> (~~ (find-module 'gauche) (find-module 'util.list) 'table 'cond-list ) (#<gloc gauche#cond-list> #<gloc util.list#cond-list>)
(fliprはGauche:コンビネータプログラミングを参照。)
; (flip~ 'hoge 'foo x) = (~ x 'hoge 'foo) (define (flip~ . arg) (apply flipr ~ arg) )
(いまひとつ単純になっていないので改良の余地ありです。)yamasushi
; ~head-propagate$
; procの第一引数にobjを参照したものを部分適用したものを返す
(define (~head-propagate$ proc obj . selectors)
(pa$ proc (apply ~ obj selectors) ) )
(define (slot-head-propagate$ proc obj . selectors)
(pa$ proc (apply slot-ref* obj selectors) ) )
; ~tail-propagate$
; procの最後にobjを参照したものを部分適用したものを返す
(define (~tail-propagate$ proc obj . selectors)
(^ arg
(apply proc `(,@arg ,(apply ~ obj selectors)))))
(define (slot-tail-propagate$ proc obj . selectors)
(^ arg
(apply proc `(,@arg ,(apply slot-ref* obj selectors)))))
(define (~$ . selectors) (cut apply ~ <> selectors) ) (define (slot-ref*$ . selectors) (cut apply slot-ref* <> selectors) )
; ~の分配
; TODO setterはどうする???
; (*~ `(,x ,y) 'a 'b) --> `(,(~ x 'a 'b) ,(~ y 'a 'b))
(define *~
(match-lambda*
[ ( (objs ... ) . selectors ) (map (cut apply ~ <> selectors ) objs) ]
[ (obj . selectors) (apply ~ obj selectors) ] ) )
; slot-refの分配
; TODO setterはどうする???
(define *slot-ref
(match-lambda*
[ ( (objs ... ) . selectors ) (map (cut apply slot-ref* <> selectors ) objs) ]
[ (obj . selectors) (apply slot-ref* obj selectors) ] ) )
; ~で参照したオブジェクトを比較する関数をつくる (define (~compare$ cmp . keys) (^(x y) (cmp (apply ~ x keys) (apply ~ y keys) ) ) ) ; 指定スロットでオブジェクトを比較する関数をつくる (define (slot-compare cmp key x y) (cmp (slot-ref x key) (slot-ref y key) ) ) (define (slot-compare$ cmp key) (pa$ slot-compare cmp key) ) (define (slot-compare*$ cmp . keys) (^(x y) (cmp (apply slot-ref* x keys) (apply slot-ref* y keys) ) ) )
(※ pack$ はGauche:コンビネータプログラミングにコードがあります。)
; 万能アクセサに値を通して、指定手続きで固める手続きを返す
; TODO ~の多段参照にも対応するか?
(define (~pack$ packer . keys)
(apply pack$ packer
(map (^k (cut ~ <> k)) keys) ) )
(define (slot-ref-pack$ packer . keys)
(apply pack$ packer
(map (^k (cut slot-ref <> k)) keys) ) )
Tag: gauche.procedure