recursividad - Funciones de devolución de llamada de Javascript y recursión
recursividad ejemplos (4)
¿Qué tal esto?
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));
});
}
Cuantas cadenas de respaldo quieras Simplemente llámalo con un 0 como el segundo argumento la primera vez.
Esta es una especie de pregunta de acertijo, ya que el código funciona perfectamente bien tal como está, simplemente irrita mi sentido estético muy levemente. Estoy recurriendo a Stack Overflow porque mi propio cerebro me está fallando en este momento.
Aquí hay un fragmento de código que busca una dirección usando la API JS de Google Maps y coloca un marcador en un mapa. Sin embargo, a veces la búsqueda inicial falla, por lo que quiero repetir el proceso con una dirección diferente.
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));
})
(El segundo parámetro para getLatLng
es una función de devolución de llamada).
Por supuesto, puede ver que las tres líneas que se centran y hacen zoom en el mapa y agregan el marcador están duplicadas, una vez en la devolución de llamada primaria y una vez en la "devolución de llamada alternativa" (ja, ja). ¿Puedes encontrar una manera de expresar todo sin redundancia? Usted gana puntos de bonificación, y mi adulación, si su solución funciona para una cantidad arbitraria de cadenas de mapas de respaldo.
Las otras respuestas son buenas, pero aquí hay una opción más. Esto le permite mantener la misma forma con la que comenzó pero utiliza el truco de nombrar su función lambda para que pueda consultarla recursivamente:
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?
}
})
La primera vez que se utiliza el símbolo "lambda", es introducirlo como un nuevo nombre literal de función. La segunda vez que se usa, es una referencia recursiva.
El nombre literal de la función funciona en Chrome, y supongo que funciona en la mayoría de los navegadores modernos, pero no lo he probado y no conozco navegadores más antiguos.
Sí, descomponerlo en una función :)
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);
});
Existe un método extremadamente agradable para realizar recursiones en construcciones de lenguaje que no admiten explícitamente la recursión llamada combinador de punto fijo . El más conocido es el Y-Combinator .
Aquí está el combinador Y para una función de un parámetro en Javascript :
function Y(le, a) {
return function (f) {
return f(f);
}(function (f) {
return le(function (x) {
return f(f)(x);
}, a);
});
}
Esto parece un poco aterrador, pero solo tienes que escribir eso una vez. Usarlo es bastante simple. Básicamente, tomas tu lambda original de un parámetro y la conviertes en una nueva función de dos parámetros: el primer parámetro es ahora la expresión lambda real en la que puedes hacer la llamada recursiva, el segundo parámetro es el primer parámetro original ( point
) que desea usar.
Así es como puedes usarlo en tu ejemplo. Tenga en cuenta que estoy usando mapstrings
como una lista de cadenas para buscar y que la función pop eliminaría de forma destructiva un elemento del encabezado.
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));
});