Foros del Web » Programación para mayores de 30 ;) » Java »

[SOLUCIONADO] Problema al borrar en árbol Set casero

Estas en el tema de Problema al borrar en árbol Set casero en el foro de Java en Foros del Web. Esta es la continuación de este post: Balanceos con árboles set . Este es el problema, resulta que tengo el siguiente error: Cita: java.lang.Exception at ...
  #1 (permalink)  
Antiguo 26/10/2018, 11:53
Avatar de detective_jd  
Fecha de Ingreso: abril-2011
Ubicación: Salto
Mensajes: 437
Antigüedad: 13 años
Puntos: 6
Problema al borrar en árbol Set casero

Esta es la continuación de este post: Balanceos con árboles set.

Este es el problema, resulta que tengo el siguiente error:

Cita:
java.lang.Exception
at treesetsimple.test.Test.comprobar_que(Test.java:6)
at treesetsimple.test.DownTest.probando_borrado(DownT est.java:36)
at treesetsimple.test.DownTest.test(DownTest.java:44)
at treesetsimple.interfaz.Cuerpo.main(Cuerpo.java:13)
Lo que no me doy cuenta es dónde está la falla del remove de MyTreeMap porque el primer elemento lo borra pero el siguiente no, sacando a Franco de la línea 43 de la clase DownTest.java anda, por alguna razón no me borra Franco:

Código Java:
Ver original
  1. probando_borrado(restart(), new Object[]{"Deborah","Franco","Denisse"});

Este es el remove de MyTreeSet (en las líneas 52, 53 y 54) que delega de MyTreeMap:

Código Java:
Ver original
  1. @Override
  2.     public boolean remove(Object o) {
  3.         return map.remove(o) == PRESENT;
  4.     }

Es el remove de MyTreeMap.java dónde supuestamente estaría el error:

Código Java:
Ver original
  1. @Override
  2.     public V remove(Object key) {
  3.         Entry<K,V> p = getEntry(key);
  4.         if (p == null) {
  5.             return null;
  6.         } else {
  7.             V oldValue = p.value;
  8.             deleteEntry(p);            
  9.             return oldValue;
  10.         }
  11.     }
  12. private void deleteEntry(Entry<K,V> p) {
  13.         size--;
  14.         Entry<K,V> tmp = new Entry();
  15.         Entry<K,V> y = (leftOf(p) == null || rightOf(p) == null) ? p : higherEntry(p.getKey());
  16.         Entry<K,V> x = (leftOf(y) != null) ? leftOf(y) : (rightOf(y) != null ? rightOf(y) : tmp);        
  17.         x.parent = parentOf(y);
  18.         if (parentOf(y) == null) {
  19.             root = (x == tmp ? null : x);
  20.         } else {
  21.             if (y == leftOf(parentOf(y))) {
  22.                 y.parent.left = (x == tmp) ? null : x;
  23.             } else {
  24.                 y.parent.right = (x == tmp) ? null : x;
  25.             }
  26.         }
  27.         if (y != p){
  28.             p = y;
  29.         }
  30.         if (colorOf(y) == BLACK) {
  31.             fixDown(x);
  32.         }
  33.     }
  34. private void fixDown(Entry<K,V>x){
  35.         while(x != root && colorOf(x) == BLACK) {
  36.             if(x == parentOf(leftOf(x))) {                
  37.                 Entry<K,V> sib = parentOf(rightOf(x));
  38.                 if(colorOf(sib) == RED) {
  39.                     setColor(sib, BLACK);
  40.                     setColor(parentOf(x), RED);
  41.                     rotateLeft(x.parent);
  42.                     sib = parentOf(rightOf(x));
  43.                 }
  44.                 if(colorOf(leftOf(sib)) == BLACK && colorOf(rightOf(sib)) == BLACK) {
  45.                     setColor(sib, RED);
  46.                     x = parentOf(x);
  47.                 } else {
  48.                     if(colorOf(rightOf(sib)) == BLACK) {
  49.                         setColor(leftOf(sib), BLACK);
  50.                         setColor(sib, RED);
  51.                         rotateRight(sib);
  52.                         sib = rightOf(parentOf(x));
  53.                     }
  54.                     setColor(sib, colorOf(parentOf(x)));
  55.                     setColor(parentOf(x), BLACK);
  56.                     setColor(rightOf(sib), BLACK);
  57.                     rotateLeft(x.parent);
  58.                     x = root;
  59.                 }
  60.             } else {
  61.                 Entry<K,V> sib = leftOf(parentOf(x));                
  62.                 if(colorOf(sib) == RED) {
  63.                     setColor(sib, BLACK);
  64.                     setColor(parentOf(x), RED);
  65.                     rotateRight(x.parent);
  66.                     sib = leftOf(parentOf(x));
  67.                 }
  68.                 if(colorOf(rightOf(sib)) == BLACK && colorOf(leftOf(sib)) == BLACK){
  69.                     setColor(sib, RED);
  70.                     x = parentOf(x);
  71.                 } else {                    
  72.                     if(colorOf(leftOf(sib)) == BLACK) {
  73.                         setColor(rightOf(sib), BLACK);
  74.                         setColor(sib, RED);
  75.                         rotateLeft(sib);                        
  76.                         sib = leftOf(parentOf(x));
  77.                     }
  78.                     setColor(sib, colorOf(parentOf(x)));
  79.                     setColor(parentOf(x), BLACK);
  80.                     setColor(leftOf(sib), BLACK);
  81.                     rotateRight(x.parent);
  82.                     x = root;
  83.                 }
  84.             }
  85.         }
  86.         setColor(x, BLACK);
  87.     }

Puse los métodos que usa internamente remove por si la falla está en dónde menos espero.
Espero sus respuestas y saludos.
__________________
Si te interesa, visita mi perfil de Linkedin. Gracias
  #2 (permalink)  
Antiguo 26/10/2018, 15:00
 
Fecha de Ingreso: junio-2008
Ubicación: Seattle, USA
Mensajes: 733
Antigüedad: 15 años, 10 meses
Puntos: 61
Respuesta: Problema al borrar en árbol Set casero

Haz (sl menos) 1 test minimo que falle.
No insertes 5 elementos si falla con 2.
Has probado la implementacion de tu Map? Funciona bien? Esta es una pregunta retorica. Claramente no has probado bien la implementacion que estas usando para construir tu Set.

Cuando publiques, sirve mas que expliques que tests estas haciendo EXPLICANDOLO lo que pruebas, no haciendo referencia a lineas de codigo o a cosas que solo tu entiendes.

Ejemplo de explicacion malo:
(COPIAR EL STACK TRACE) y luego ...
"Lo que no me doy cuenta es dónde está la falla del remove de MyTreeMap porque el primer elemento lo borra pero el siguiente no, sacando a Franco de la línea 43 de la clase DownTest.java anda, por alguna razón no me borra Franco:"

Ejemplo de explicacion mejor:
Para probar, inserto inicialmente varios elementos en un set, y luego borro 1 por 1, preguntando una vez que se borra si el elemento sigue en el set.
El test pasa si para todos los elementos que borro, se cumple esa condicion.
Si el elemento sigue alli despues de insertar, el test falla.

Esto es para que tengas material para que descubras tu mismo tus errores, o bien que lo puedas testear y explicar mejor.
__________________
Visita mi perfil en LinkedIn
  #3 (permalink)  
Antiguo 26/10/2018, 20:54
Avatar de detective_jd  
Fecha de Ingreso: abril-2011
Ubicación: Salto
Mensajes: 437
Antigüedad: 13 años
Puntos: 6
Respuesta: Problema al borrar en árbol Set casero

Hola CalgaryCorpus gracias por responder, en base a lo que escribiste va esto:

Cita:
Has probado la implementacion de tu Map? Funciona bien? Esta es una pregunta retorica. Claramente no has probado bien la implementacion que estas usando para construir tu Set.
Resulta que la línea 351 de MyTreeMap.java es la del problema pero ahora la pregunta es: ¿cómo cambio esa línea para que se adapté la funcionalidad? porque en estos momentos no sé me ocurre nada al respecto.

Cita:
Haz (sl menos) 1 test minimo que falle.
En la línea 43 cuando llamo al método probando_borrado con sus parámetros es el único que anda mal, ya que comenté esa línea, corre el código y lo demás anda bien.

Cita:
No insertes 5 elementos si falla con 2.
En las líneas 48, 50 y 51 de DownTest.java agrega probando menos cantidad de elementos, comentando la 43 como había dicho funciona bien el resto.
Lo demás que escribíste me sirve, porque un defecto que tengo es que me digo a mi mismo "no hace falta tanta explicación debido que para otros es muy obvio lo que hago" y en eso estoy equivocado, en pocas no soy muy expresivo.

Pongo el código, porque está todo ahí para ver lo que digo: TreeSet Casero

Espero sus respuestas y saludos.
__________________
Si te interesa, visita mi perfil de Linkedin. Gracias
  #4 (permalink)  
Antiguo 26/10/2018, 21:26
 
Fecha de Ingreso: junio-2008
Ubicación: Seattle, USA
Mensajes: 733
Antigüedad: 15 años, 10 meses
Puntos: 61
Respuesta: Problema al borrar en árbol Set casero

Yo no voy a resolver esto por ti,

Te voy a explicar por n+1 vez y espero que no sea otra vez que decides que no tienes que hacer nada y tu respuesta sea un monton de numeros de linea de codigo como lo hiciste recien.

Si tienes un test que usa 5 elementos (recuerdas que te he dicho esto de los 5 elementos antes?), pero tambien ese mismo test fallaria con menos elementos, entonces tu test de 5 elementos esta metiendo ruido no mas. Tienes que hacer que tus tests sean MINIMOS, si tu problema se presenta con 2 elementos, entonces 2 elementos tiene que tener tu test. Entiendes por que tienen que ser minimos? Por que asi es mas facil depurarlos, seguirle la pista. Ademas tienen que probar solo 1 cosa, si prueban varias cosas simultaneamente tambien es un ruido. Solo 1 cosa y minimos.

Dices que el error se presenta con 5 elementos y si borras tal linea funciona y si borras alla o incluyes alla otra linea de codigo no funciona? Esto NO ES manera de depurar. A mi no me importa esto.

Haz que tus tests sean MINIMOS.

Una vez que tus tests sean minimos, usa esos test minimos para depurar.

Si tu problema esta en los Maps, y no en los Sets que usan los maps, de nuevo te pregunto, tienes tests minimos para esta estructura? Seguro que no, porque esos tests deberian fallar y con eso resolverias tu problema con el set.

Yo apostaria que los otros tests que tienes son del mismo estilo, conjunto de datos gigantes, que prueban varias cosas, etc. Asi que una vez que todos tus tests pasen, lo mas probable es que no haya certeza aun que has probado correctamente.

Te habia dicho que los tests tienen que ser minimos?
Y que da lo mismo si el test que tienes falla si pones una linea aqui o borras una linea alla?

Voy a esperar tu respuesta para ver que dices esta vez.

Si resuelves el problema, vuelve al foro y explica que' era el problema y como lo resolviste, pero por favor que no sea diciendo, "borre esta linea", o "la linea 43 la movi 2 lineas arriba y la 58 la movi hacia abajo", eso no es explicar.

que sea algo como
"estaba insertando 2 veces y esta duplicacion afectaba aqui o alla",
"internamente tenia los datos desordenados y el codigo suponia que los datos estaban ordenados"

Tambien sirve poner codigo RELEVANTE antes de la modificacion, codigo RELEVANTE despues. No kilometros de codigo y vea usted estimado lector donde estaba el error.
__________________
Visita mi perfil en LinkedIn
  #5 (permalink)  
Antiguo 23/11/2018, 21:45
Avatar de detective_jd  
Fecha de Ingreso: abril-2011
Ubicación: Salto
Mensajes: 437
Antigüedad: 13 años
Puntos: 6
Respuesta: Problema al borrar en árbol Set casero

Buenas GalgaryCorpus, resulta que el error estaba dónde menos lo pensaba y es a la hora de ordenar las entradas a eliminar:

Sí lo mando a eliminar en este orden:

Código Java:
Ver original
  1. probando_borrado(restart(), new Object[]{"Deborah","Franco","Denisse"});

Me da el error:

Cita:
java.lang.Exception
at treesetsimple.test.Test.comprobar_que(Test.java:6)
at treesetsimple.test.DownTest.probando_borrado(DownT est.java:35)
at treesetsimple.test.DownTest.test(DownTest.java:43)
at treesetsimple.interfaz.Cuerpo.main(Cuerpo.java:13)

Pero si lo pongo en este orden:

Código Java:
Ver original
  1. probando_borrado(restart(), new Object[]{"Deborah","Denisse","Franco"});

Funciona sin problemas, lo que tendremos que ver es cómo hacer para que permita borrar sin que interfiera el órden de entradas a borrar, otra las claves son strings si me entiendes.

Espero sus respuestas y saludos.

PD: n+2;
__________________
Si te interesa, visita mi perfil de Linkedin. Gracias
  #6 (permalink)  
Antiguo 26/11/2018, 03:17
Avatar de Fuzzylog  
Fecha de Ingreso: agosto-2008
Ubicación: En internet
Mensajes: 2.511
Antigüedad: 15 años, 8 meses
Puntos: 188
Respuesta: Problema al borrar en árbol Set casero

Me da en el hocico que en algún momento estás modificando el tamaño de algún set (añadiendo o quitando elementos) a la vez que lo iteras. Deberías usar una copia del set para modificarlo y luego sustituir la copia modificada una vez finalizada la iteración.
__________________
if (fuzzy && smooth) {
fuzzylog = "c00l";
return true;
}
  #7 (permalink)  
Antiguo 26/11/2018, 21:22
Avatar de detective_jd  
Fecha de Ingreso: abril-2011
Ubicación: Salto
Mensajes: 437
Antigüedad: 13 años
Puntos: 6
Respuesta: Problema al borrar en árbol Set casero

Hola FuzzyLog por responder, hice lo que me dijiste y no funcionó:

Código Java:
Ver original
  1. private void probando_borrado(Object[]arreglo, Object[]criterio) throws Exception{
  2.             set.clear();
  3.             for (Object obj : arreglo) {
  4.                 set.add(obj.toString());
  5.             }            
  6.             MyTreeSet<String>aux = set;
  7.             for(Object c : criterio){
  8.                 if(c == null){
  9.                     aux.remove(null);
  10.                     this.comprobar_que(!set.contains(null));
  11.                 } else {
  12.                     aux.remove(c.toString());
  13.                     this.comprobar_que(!set.contains(c.toString()));
  14.                 }                        
  15.             }
  16.         }

Depuré el código y aquí esta el resultado:

Cita:
El órden después de insertar los string al set:

"Deborah" < - > "Denisse" < - > "Franco" < - > "Manuela" < - > "Miguel" < - > "Tommy"

Cuando quiero borrar a "Franco"

línea 343 : decrementa el tamaño a 1 menos
línea 351 : y tiene la entrada "Manuela"
línea 359 : x es nulo
línea 361 : el pariente de x pasa a ser Miguel
linea 367 : como el pariente de x no es nulo, entra en la contradicción (else)
línea 377 : como y es igual al izquierdo del pariente de y, entra en la condición (if)
línea 378 : el izquierdo del pariente de y, pasa a ser nulo
línea 389 : al ser distintos (y) y (p), entra en la condición
línea 390 : p toma el valor de y
línea 395 : como el color de y no es negro, no entra en el if.
El problema parece ser el órden de los datos a eliminar y ahí es dónde no me doy cuenta que arreglar si el deleteEntry (que parece lo más evidente a arreglar) o que.

Pondré el código del deleteEntry de MyTreeMap.java, ya que puede que no mires el repositorio de github, pero otro lado por dónde mirarlo no se me ocurre:

Código Java:
Ver original
  1. private void deleteEntry(Entry<K,V> p) {
  2.         // Decrementa la cantidad de elementos en el árbol.
  3.         size--;
  4.         // Creamos una nueva entrada vacía.
  5.         Entry<K,V> tmp = new Entry();
  6.         /*
  7.             Creamos una entrada llamada y y mediante un operador ternario si
  8.             la izquierda o derecha de p es nula que y tenga el valor de p de
  9.             lo contrario de obtenga al siguiente de p.
  10.         */
  11.         Entry<K,V> y = (leftOf(p) == null || rightOf(p) == null) ? p : higherEntry(p.getKey());
  12.         /*
  13.             Luego creo otra entrada llamada x y mediante operador ternario
  14.             si la izquierda de y no es nula, x tendrá como valor la izquierda
  15.             de y de lo contrario otro operador ternario dentro del primero que
  16.             si la derecha de y no es nula que x obtenga como valor la derecha
  17.             de y  de lo contrario obtendrá la entrada vacía.
  18.         */
  19.         Entry<K,V> x = (leftOf(y) != null) ? leftOf(y) : (rightOf(y) != null ? rightOf(y) : tmp);        
  20.         // Al pariente de x se le asignará como valor el pariente de y
  21.         x.parent = parentOf(y);
  22.         /*
  23.             Sí el pariente de y es nulo, la raíz del árbol se le asignará como
  24.             valor mediante operador ternario que si x es una entrada vacía
  25.             la raíz será nula de lo contrario x será el valor de la raíz
  26.         */
  27.         if (parentOf(y) == null) {
  28.             root = (x == tmp ? null : x);
  29.         } else {
  30.             /*
  31.                 Ahora si el pariente de y no es nulo, entramos en otra
  32.                 condición que si y es igual al izquierdo del pariente de y
  33.                 el izquierdo del pariente de y tendrá como valor mediente
  34.                 operador ternario si x es una entrada vacía será nulo sino
  35.                 su valor será x
  36.             */
  37.             if (y == leftOf(parentOf(y))) {
  38.                 y.parent.left = (x == tmp) ? null : x;
  39.             } else {
  40.                 /*
  41.                     De lo contrario el derecho del pariente de y tendrá como
  42.                     valor por operador ternario si x es una entrada vacía
  43.                     su valor será nulo sino será x
  44.                 */
  45.                 y.parent.right = (x == tmp) ? null : x;
  46.             }
  47.         }
  48.         // Si y es distinto de p, a p se le asignará como valor y
  49.         if (y != p){
  50.             p = y;
  51.         }
  52.         /*  Y si el color de y es negro, el árbol será balanceado para
  53.             actualizarse luego de la eliminación
  54.         */
  55.         if (colorOf(y) == BLACK) {
  56.             fixDown(x);
  57.         }
  58.     }

Espero sus respuestas y saludos.
__________________
Si te interesa, visita mi perfil de Linkedin. Gracias
  #8 (permalink)  
Antiguo 27/11/2018, 06:34
 
Fecha de Ingreso: junio-2008
Ubicación: Seattle, USA
Mensajes: 733
Antigüedad: 15 años, 10 meses
Puntos: 61
Respuesta: Problema al borrar en árbol Set casero

Sugiero una de dos opciones:

- que revises bien en que parte te has desviado de la implementacion de la openjdk que ese codigo evidentemente esta inspirado. Haciendo un diff visual del codigo de la openjdk con tu implementacion, sin entenderlo siquiera, noto que hay unos desvios evidentes que posiblemente expliquen por que' tu codigo no funciona.

o bien

- Reimplementa el arbol que esta detras de esto. Esta vez si' lo haces tu, desde cero. Con ello vas a poder realmente explicar el codigo, sera "casero" como le has llamado, y no vas a hacer ingenieria reversa para poder entenderlo. Olvidate del balanceo inicialmente. Puedes volver a esto una vez que el arbol ya funcione. En esa implementacion haz test minimos. Creo que vas a aprender mas asi.
__________________
Visita mi perfil en LinkedIn
  #9 (permalink)  
Antiguo 27/11/2018, 06:44
Avatar de Fuzzylog  
Fecha de Ingreso: agosto-2008
Ubicación: En internet
Mensajes: 2.511
Antigüedad: 15 años, 8 meses
Puntos: 188
Respuesta: Problema al borrar en árbol Set casero

Si quieres respuestas mejor aclara las preguntas.

Escribes sobre cosas muy diversas y en apariencia inconexas.

Por ejemplo tu método deleteEntry no tiene que ver con la excepción de la que hablabas al inicio del post.

Por tanto...

1. Cuál es el problema que tienes. Si es más de uno explícalos por separado.
2. Acostúmbrate a hacer debug desde tu entorno de desarrollo y ver los valores que tienen los objetos antes de que salte el error. Así tendrás una idea de por qué pasan.
__________________
if (fuzzy && smooth) {
fuzzylog = "c00l";
return true;
}
  #10 (permalink)  
Antiguo 27/11/2018, 20:07
Avatar de detective_jd  
Fecha de Ingreso: abril-2011
Ubicación: Salto
Mensajes: 437
Antigüedad: 13 años
Puntos: 6
Respuesta: Problema al borrar en árbol Set casero

CalgaryCorpus y FuzzyLog gracias por responder, resolví el problema resulta que era un error al escribir código, revisé el código que tenía con el de Chuck McManis en la parte del borrar, lo adapté y funcionó.

Bueno esto será todo con los Hashing casero, lo último que me queda por hacer es una pequeña inspiración tuve para adaptar a MyLinkedMap y MyLinkedSet, y después de eso sí.
Empezaré lo que quería hacer desde un principio pero no sabía cómo funcionaban bien los Map y Set.

Muchas gracias por su tiempo y saludos.

Repositorio TreeMap casero -> http://bit.ly/2BDgpVb

Repositorio TreeSet casero -> http://bit.ly/2Si7YnU

PD: ¿cómo se inicia un post para hacer aportes en foros del web?
__________________
Si te interesa, visita mi perfil de Linkedin. Gracias
  #11 (permalink)  
Antiguo 08/12/2018, 21:21
Avatar de detective_jd  
Fecha de Ingreso: abril-2011
Ubicación: Salto
Mensajes: 437
Antigüedad: 13 años
Puntos: 6
Respuesta: Problema al borrar en árbol Set casero

Bueno aquí esta la inspiración que tuve resulta que logré simular las versiones actuales de LinkedHashMap y LinkedHashSet al ponerles dobles punteros de inicio y fin y hacerlo tipo lista doble enlazada además de poner funciones para acceder a los mismos.

Repositorio LinkedHashMap casero -> http://bit.ly/2G7V2zC

Repositorio LinkedHashSet casero -> http://bit.ly/2RL3BSl

Saludos.
__________________
Si te interesa, visita mi perfil de Linkedin. Gracias

Etiquetas: casero, post, set
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 00:38.