16

这是一个脑筋急转弯的问题,因为代码按原样工作得非常好,它只是稍微刺激了我的审美意识。我转向 Stack Overflow 是因为我自己的大脑现在让我失望了。

这是一段使用 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));
})

(第二个参数getLatLng是回调函数。)

当然,您可以看到使地图居中和缩放地图以及添加标记的三行是重复的,一次在主回调中,一次在“后备回调”中(哈哈)。你能找到一种方法来表达整个事情而没有任何冗余吗?如果您的解决方案适用于任意数量的备份地图字符串,您将获得奖励积分和我的赞美。

4

4 回答 4

21

其他答案很好,但这里还有一个选择。这使您可以保持与开始时相同的形式,但使用命名 lambda 函数的技巧,以便您可以递归地引用它:

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?
    }
})

第一次使用符号“lambda”,是为了引入它作为一个新的函数字面量名称。第二次使用时,是递归引用。

函数字面量命名适用于 Chrome,我认为它适用于大多数现代浏览器,但我没有测试过它,我不知道旧浏览器。

于 2008-11-09T06:25:25.327 回答
8

有一种非常好的方法可以在不明确支持递归的语言结构中执行递归,称为定点组合器。最著名的是Y-Combinator

这是 Javascript 中一个参数的函数的 Y 组合器

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

这看起来有点吓人,但你只需要写一次。使用它实际上非常简单。基本上,您使用一个参数的原始 lambda,并将其转换为两个参数的新函数 - 第一个参数现在是您可以进行递归调用的实际 lambda 表达式,第二个参数是原始的第一个参数 ( point) 你想使用的。

这就是您在示例中使用它的方式。请注意,我将mapstrings其用作要查找的字符串列表,并且 pop 函数会破坏性地从头部删除一个元素。

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));
                       });
}

尽可能多的备份字符串。只需第一次使用 0 作为第二个参数调用它。

于 2008-11-09T04:00:24.667 回答