2012-03-08 14 views
7

supongamos que tengo una función que se arrastra sobre una gran variedad ...¿Detecta recursión infinita?

flatten([a, b, c, d, [e, f, g, [h, i, j, k], l], m, n, o, p]) 
>> [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p] 

Aplanar se arrastraría sobre el código y para cada matriz se encuentran de forma recursiva entrar en esa matriz y devolver los valores tales que usted tiene un piso formación.

Esto funciona hasta que tengamos una matriz tales como:

a = []; 
a[0] = a; 

Obviamente, esto crea una recursión infinita:

Array[1] 
0: Array[1] 
0: Array[1] 
    0: Array[1] 
    0: Array[1] 
    0: Array[1] 
    0: Array[1] 
     ... 

¿Cómo puedo detectar este comportamiento sin modifiying la matriz de modo que la función se puede tratar ¿con este?

+0

¿Podemos mantener lo que hemos rastreado, y si ya hemos rastreado esa matriz, entonces estamos en la situación descrita anteriormente ... –

+0

Mira [esta publicación] (http: // stackoverflow .com/a/9386208/989121). Básicamente muestra cómo escribir un decorador de función que limita la profundidad de recursión. – georg

Respuesta

2

Deberá mantener una serie de arreglos visitados dentro de la función flatten() y verificar su existencia antes de que vuelva a insultarlo. Sin embargo, tendría que pasar la lista de elementos visitados como un segundo parámetro al recurse.

function recurse(ar, visited) { 
    visited = visited || []; 

    function isVisited(obj) { 
     for (var i=0;i<visited.length;i++) { 
      if (visited[i] == obj) { 
       return true; 
      } 
     } 

     visted.push(obj); // but we've visited it now 
     return false; 
    } 

    // blah blah blah, do your thing... check isVisted[i] 
} 

Esto resultará costoso para comprobar si la matriz es profunda, por lo que podría engañar y establecer un atributo de cada matriz se visita, y comprobar por ella (pero entonces, por supuesto, que se está modificando la matriz (pero no dramáticamente)).

function recurse(ar) { 
    function isVisited(obj) { 
     var key = 'visited'; 
     var visited = obj.hasOwnProperty(key); 

     obj[key] = true; // but we've visited it now 

     return visited; 
    } 

    // blah blah blah, do your thing... check isVisted[i] 
} 
+0

Una vez que los mapas débiles estén estandarizados/ampliamente disponibles, serían una buena alternativa para agregar una propiedad a cada conjunto. Ver http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps#cycle-tolerant_graph_walking –

5

Si la detección de la recursividad es un requisito, vas a tener que negociar el espacio de memoria para ello: crear una matriz de objetos que Analizada (los parámetros se envían de forma recursiva) y comprobar cada nuevo parámetro en contra de ella. Si ya lo analizó, regrese inmediatamente.

+1

+1. Además, ordene la matriz si puede (no estoy familiarizado con JavaScript y su representación de objetos, pero supongo que puede tomar la dirección de las cosas), para que pueda usar la búsqueda binaria, o tal vez alguna otra estructura de datos que se ajuste a la trabajo mejor que una matriz. – Guido

+0

Mi primer pensamiento fue decir que no puedes obtener el id. Numérico para objetos como ese, pero de hecho estoy decentemente seguro de que puedes; excelente sugerencia! – Blindy

+0

puede usar un conjunto, un objeto cuyas teclas representan los elementos del conjunto, y los valores para esas teclas pueden ser 'null' (o algún otro objeto simulado) – newacct

2

Una manera conveniente de rastrear arreglos ya visitados sería agregar una propiedad "plana" a cada uno, tal vez estableciendo un valor único para cada operación. No tiene sentido jugar con un mapa por separado cuando cada matriz ya es un objeto perfectamente bueno.

+0

Verdadero si posee el objeto (es decir, puede cambiar libremente como quieras), pero ese no siempre es el caso. Además, tendrías que recorrerlo dos veces para restablecer la propiedad 'plana 'de nuevo a falso (y luego tendrías que tratar con la recursión nuevamente), o no podrías aplanar un objeto dos veces. – Blindy

+0

Sí, siempre se puede ir a limpiar el marcador después, supongo. – Pointy

0

En el mundo real, el paso 1 es encontrar al desarrollador ofensor que le entregó un objeto que se refería a sí mismo y golpearlo con un palo.

El problema se resuelve, sin embargo, eliminándolos como autorreferencias en su lugar. Conviértelos en copias. Array.slice devuelve partes de las matrices (incluidas las copias completas) sin alterar el original.

if(thisElement.constructor.prototype.slice){ //is it an array 
    thisElement = thisElement.slice(0); //now it's a new but identical array 
} 

Tenga en cuenta que esto es solo en la referencia de matriz principal. Las referencias de matrices interiores aún deberán modificarse, ya que una referencia copiada aún apunta a la misma cosa.

Además, si puede hacer suposiciones sobre los personajes que definitivamente no estarán presentes, puede encontrar que cortar/unir es muy útil.

1

Otra forma más sucia (pero bueno para el mínimo esfuerzo) es apenas utilizar el codificador JSON:

var is_recursive = false; 

try { 
    JSON.stringify(my_recursive_object); 
} catch (e) { 
    if (e.name = 'TypeError') 
     is_recursive = true; 
    else 
     throw e; 
} 

he encontrado esta pregunta en busca de una mejor respuesta, aunque, - aunque esto puede ayudar a alguien que quiera un buen truco ;-)

Cuestiones relacionadas