トップ «前の日記(2005-04-29 (Fri)) 最新 次の日記(2005-05-05 (Thu))» 編集 RSS feed

継続にっき

2004|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|12|
2006|01|03|05|06|08|09|10|12|
2007|01|02|03|05|07|12|
2008|10|
2009|01|05|12|
2010|04|05|11|
2011|01|09|12|
2012|02|03|05|09|12|
2013|02|03|
2014|05|09|
2015|12|
2017|09|

2005-05-03 (Tue)

))) クロージャ

クロージャは理解したものだと思っていたけど軽くはまった。

お題
引数nを渡したときに、その戻り値rが r[i]() == i (ただし 0<=i<n)を満たす配列となるような関数fを定義せよ。

字面通りにJavaScriptで書くと困ったことになる。

function f(n) {
    var result = new Array();
    for (var i = 0; i < n; i++) {
        result.push(function () { return i});
    }
    return result;
}

var r = f(5);
for (var i = 0; i < r.length; i++) {
    r[i](); // => 5
}

これは関数fの中でiへの束縛が1回しか行われていないのが原因。 どんどん新しい束縛を導入するような言語なら起こりにくい。

# Ruby
def f(n)
  # ここにi=nilとかやると同じ現象になる
  (0...n).inject([]) {|r,i| r << lambda{i} }
end

;; Scheme
(define (f n)
        (let loop ((i (- n 1)) (r '()))
          (if (< i 0)
              r
            (loop (- i 1) (cons (lambda () i) r)))))

で、本題はJavaScriptでこれにどう対処するか。問題の箇所にもう一段階functionを被せることで 期待通りに動くようにはなるけど他の手はないんだろうか。

result.push(function (j) {return function () { return j}}(i));

))) クロージャ(2)

JavaScriptならば起こりやすいとは言ってもこのぐらいなら分かるよなー、と思ってもう一回コードを見てみたら 少し違ってた。

function f(n) {
    var result = new Array();
    for (var i = 0; i < n; i++) {
        var j = i;
        result.push(function () { return j});
    }
    return result;
}

forループの中でさらに var j なんてことをやってた(ちなみにこれだとr[i]()はすべて4になる)。 これなら(Rubyからの切り替えに失敗したと考えれば)はまるのも理解できる。


2004|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|12|
2006|01|03|05|06|08|09|10|12|
2007|01|02|03|05|07|12|
2008|10|
2009|01|05|12|
2010|04|05|11|
2011|01|09|12|
2012|02|03|05|09|12|
2013|02|03|
2014|05|09|
2015|12|
2017|09|
トップ «前の日記(2005-04-29 (Fri)) 最新 次の日記(2005-05-05 (Thu))» 編集 RSS feed