糖衣構文でもあれば便利だろうなぁと思う場面が山ほどあるので、是非早いとこ実装してもらいたいが(JDK7では見送られた)。
話に参加していたもう一人の同僚が、違いが分からないと言っていたので、関数型言語のクロージャーとどう違うのかというのでも書いてみようかなぁと。ただ、僕自身あんまり理解してないので間違いは山ほどあるかと・・・
そもそも、クロージャーとは何ぞやと。詳しい説明はWikipediaでも見てもらうとして、上記で議論になっていたのは捕捉時の環境が閉じているかどうかということであった。JavaやC++ではここが完全に閉じることができず、渡された自由変数が参照渡しになる。以下はJavaでそれっぽく見せるようにしたコード。(C++では補足さえ出来ない)
package closure; public interface Lambda<T> { T apply(Object... args); } // 別ファイル package closure; public class Closure { private Object obj; public void run() { // property Lambda<Object> lam = new Lambda<Object>() { @Override public Object apply(Object... args) { obj = args[0]; System.out.println(obj); return obj; } }; final Object obj2 = (Object)"string"; Lambda<Object> lam2 = new Lambda<Object>() { @Override public Object apply(Object... args) { // obj2 = args[0]; // *1 compile error System.out.println(obj2); return null; } }; System.out.println(lam.apply("here we go")); System.out.println(obj); lam2.apply(); } public static void main(String[] args) { Closure cl = new Closure(); cl.run(); } }*1の部分が重要で、Javaでは無名クラスの中から参照できる自由変数(とここでは呼ぶ)は変更不可能でなければならない。多分JVMの実装上も問題だろう。lam2がrunから戻った際にスタックからobj2が消えるのでその辺が問題になると思われる。
ではわれらがSchemeではどうだろうか?
(define obj #f) (define (runner lam1 lam2) (print (lam1 "here we go")) (print (lam2 "we can do it"))) (define (main args) (let* ((obj2 "string") (lam1 (lambda (o) (set! obj o) o)) (lam2 (lambda (o) (set! obj2 o) o))) (runner lam1 lam2) (print obj #\: obj2))) #| here we go we can do it here we go:we can do it |#あまりいい例ではない気もするが、lam2で捕捉されたobj2の値が変更されるとlambdaの外側のでも問題なく変更されている。
これが出来て何が嬉しいかと言われると正直微妙ではある。(結構使うけどこういうの)
ただ、Javaでもfinalつければ捕捉出来るんだし、ほぼ同じじゃないのだろうか?(いい加減)
(define obj #f) (define (runner o run?) (let* ((obj2 o) (lam1 (lambda (o) (set! obj o) o)) (lam2 (lambda (o) (set! obj2 o) o))) (cond (run? (print (lam1 "here we go")) (print (lam2 "we can do it")) (print obj #\: obj2)) (else (values lam1 lam2 obj2))))) (define (main args) (let ((o "string")) (runner o #t) (receive (lam1 lam2 obj2) (runner o #f) (print (lam1 "here we go")) (print (lam2 "we can do it")) (print obj #\: obj2)) (print o))) #| here we go we can do it here we go:we can do it here we go we can do it here we go:string string |#こっちの方が捕捉時の環境というのが捕らえやすいだろうか?あんまり変わらないか?
No comments:
Post a Comment