16

コードはそのままで問題なく動作するため、これは一種の頭の体操のような質問です。私自身の脳が今私を失敗させているので、私はスタックオーバーフローに目を向けています。

Google Maps JS API を使用して住所を検索し、地図上にマーカーを配置するコード スニペットを次に示します。ただし、最初のルックアップが失敗する場合があるため、別のアドレスでプロセスを繰り返したいと考えています。

geocoder.getLatLng(item.mapstring, function(point) {
    if (!point) {
        geocoder.getLatLng(item.backup_mapstring, function(point) {
            if (!point) return;
            map.setCenter(point, 13);
            map.setZoom(7);
            map.addOverlay(new GMarker(point));
        })
        return;
    }
    map.setCenter(point, 13);
    map.setZoom(7);
    map.addOverlay(new GMarker(point));
})

( の 2 番目のパラメーターgetLatLngはコールバック関数です。)

もちろん、マップを中心に合わせてズームし、マーカーを追加する 3 つの行が重複していることがわかります。1 回はプライマリ コールバックで、もう 1 回は「フォールバック コールバック」です (笑)。冗長性なしで全体を表現する方法を見つけることができますか? あなたのソリューションが任意の数のバックアップ マップ文字列に対して機能する場合、ボーナス ポイントを獲得できます。

4

4 に答える 4

21

他の答えは良いですが、ここにもう1つのオプションがあります。これにより、最初と同じ形式を維持できますが、ラムダ関数に名前を付けるというトリックを使用して、再帰的に参照できるようにします。

mapstrings = ['mapstring1', 'mapstring2', 'mapstring3'];

geocoder.getLatLng(mapstrings.shift(), function lambda(point) {
   if(point) {
        // success
        map.setCenter(point, 13);
        map.setZoom(7);
        map.addOverlay(new GMarker(point));
    }
    else if(mapstrings.length > 0) {
        // Previous mapstring failed... try next mapstring
        geocoder.getLatLng(mapstrings.shift(), lambda);
    }
    else {
        // Take special action if no mapstring succeeds?
    }
})

シンボル「ラムダ」が初めて使用されるのは、それを新しい関数リテラル名として導入するためです。2 回目に使用すると、再帰参照になります。

関数リテラルの命名は Chrome で機能し、最新のブラウザーのほとんどで機能すると思いますが、テストしていないため、古いブラウザーについてはわかりません。

于 2008-11-09T06:25:25.327 に答える
8

固定小数点コンビネータと呼ばれる、再帰を明示的にサポートしていない言語構造で再帰を実行するための非常に優れた方法があります。最もよく知られているのはY-Combinatorです。

以下は、Javascript の 1 つのパラメーターの関数の Y コンビネーターです。

function Y(le, a) {
    return function (f) {
        return f(f);
    }(function (f) {
        return le(function (x) {
            return f(f)(x);
        }, a);
    });
}

これは少し恐ろしく見えますが、一度だけ書く必要があります。それを使用することは実際には非常に簡単です。基本的に、1 つのパラメーターの元のラムダを取得し、それを 2 つのパラメーターの新しい関数に変換します。最初のパラメーターは、再帰呼び出しを実行できる実際のラムダ式になり、2 番目のパラメーターは元の最初のパラメーター ( point) を使用します。

これは、あなたの例でそれを使用する方法です。mapstrings検索する文字列のリストとして使用していて、ポップ関数がヘッドから要素を破壊的に削除することに注意してください。

geocoder.getLatLng(pop(mapstrings), Y(
  function(getLatLongCallback, point)
  {
    if (!point)
    {
      if (length(mapstrings) > 0)
        geocoder.getLatLng(pop(mapstrings), getLatLongCallback);
      return;
    }

    map.setCenter(point, 13);
    map.setZoom(7);
    map.addOverlay(new GMarker(point));
  });
于 2008-11-09T04:27:03.523 に答える
2

うん、それを関数に分解します:)

geocoder.getLatLng(item.mapstring, function(point) {
    if (!point) {
        geocoder.getLatLng(item.backup_mapstring, function(point) {
                if (point) {
                    setPoint(point);
                }
        })
        return;
    }

    function setPoint(point) {
        map.setCenter(point, 13);
        map.setZoom(7);
        map.addOverlay(new GMarker(point));
    }

    setPoint(point);
});
于 2008-11-09T04:13:36.093 に答える
1

これはどう?

function place_point(mapstrings,idx)
{
    if(idx>=mapstrings.length) return;
    geocoder.getLatLng(mapstrings[idx],
                       function(point)
                       {
                           if(!point)
                           {
                               place_point(mapstrings,idx+1);
                               return;
                           }
                           map.setCenter(point, 13);
                           map.setZoom(7);
                           map.addOverlay(new GMarker(point));
                       });
}

必要な数のバックアップ文字列。最初は 2 番目の引数として 0 を指定して呼び出すだけです。

于 2008-11-09T04:00:24.667 に答える