Fecha y hora actual: Viernes 20 Sep 2019 20:57
Índice del Foro

Foros de programación informática, diseño gráfico y Web

En esta comunidad intentaremos dar soporte de programación a todos los niveles, desde principiantes a profesionales de la informática, desarrollo de programas, programación web y mucho más.

Lección 73: Actualizando Nomina Personas - Clases Internas

Responder al Tema Ir a página 12Siguiente

Índice del Foro > Programación en general > Lección 73: Actualizando Nomina Personas - Clases Internas

Autor Mensaje
Kyshuo Ayame
Moderador Global


Registrado: 07 Ene 2011
Mensajes: 1044

Mensaje Publicado: Miércoles 31 Oct 2012 18:16

Título del mensaje: Lección 73: Actualizando Nomina Personas - Clases Internas

Responder citando

Actualizando Nomina Personas:

Como ya habrán notado en el título he tomado como ejemplo práctico aquel pequeño sistema NominaPersonas que trabajamos en Modula 2. La idea es llevar ese sistema a Java de forma progresiva, es decir, primero lo implementaremos en Java tal cual estaba implementado en Modula para luego, ir agregando funcionalidades gráficas hasta dejarlo totalmente como un sistema con interfaz gráfica para usuario final.

Un lector del curso tuvo como iniciativa aprender a utilizar el entorno Lazarus para crear interfaces gráficas tomando como ejemplo justamente este sistema. El lector entonces tomó lo dado en el curso de Modula y lo adaptó para lograr el mismo sistema pero con una interfaz gráfica interesante. Habiendo acabado su trabajo, me lo envió por correo para que yo lo probara y para que le diera mi opinión. Un trabajo excelente por donde se lo mire, más aún siendo que él aprendió por sí solo a usar las herramientas para crearlo. Esto les demuestra lo mucho que se puede lograr una vez ya estamos inmersos en el mundo de la programación. Esta persona ha decidido permanecer en el anonimato y pues respeto su postura por lo cual no he dado su nombre aquí, pero todos sepan que el diseño de la interfaz gráfica, la programación de la misma y la adaptación del código en Modula al lenguaje de Lazarus han sido realizados completamente por esta persona anónima sin que yo interviniera en absoluto, por tanto el crédito es para este amigo y lector.

A continuación les dejo algunas capturas de pantalla del sistema creado por nuestro amigo. Nosotros, con todo su permiso, tomaremos ese diseño y lo implementaremos casi idéntico en Java, salvo por algunos detalles que cambiaré yo mismo. El código fuente que desarrollaremos será creado desde cero dado que no tengo el original y tampoco hace falta ya que en sí esto será una actualización de un sistema ya creado.













Como podrán observar es un excelente trabajo. Tengan en cuenta que esa interfaz funciona, no son solo dibujos, funciona y muy bien si uno se ciñe a los requerimientos del sistema y no se pone a hacer pruebas tontas.

Pueden descargar el ejecutable en este enlace:
https://www.dropbox.com/sh/uj2mvzv79ceff3m/mWMjxl8SO3

Como dije, nosotros primero crearemos el sistema tal cual estaba creado en Modula, es decir, con un intérprete de comandos. Luego lo iremos llevando a un sistema más avanzado, mejorando poco a poco todo hasta lograr que utilice una interfaz gráfica de usuario, pero todo a su tiempo. Se que deben estar muriendo de ganas por comenzar a crear cuadros y botones, pero comprobarán luego que el retrasar eso para aprender primero todas las herramientas necesarias para comprenderlo bien dará sus frutos. Iremos avanzando poco a poco, porque por más bonita que podamos dibujar una interfaz gráfica, luego hay que darle funcionalidades, y eso no es sencillo, requiere de un manejo de la programación tal como el que estamos siguiendo hasta ahora.

Bien, entonces veamos el código fuente en Java de aquel pequeño sistemita. Lo que hice fue pasar el código exacto de Modula a la sintaxis de Java. Esto tiene algunas implicancias importantes que no deben pasarse por alto, así que les pido que no se salteen esta sección porque habrá aspectos de Java que saltarán aquí y que por tanto será donde los introduciré, luego los daré por entendidos.

--------------------------------------------------------------------------------

Nomina Personas V1.1:

Llegar a la versión 2.0 (la final en Java) será un proceso y por tanto pasaremos por varias versiones previas. La primera de ellas será la versión 1.1 que no variará en algoritmos ni en forma de uso, sino que será implementada en lenguaje Java y por tanto pasará a ser un sistema multiplataforma. Como ya dije, veremos el código exacto de Modula pasado a Java.

Clase Persona:

Se supone que ustedes ya deberían haber completado el pasaje del modulo Persona a una clase de Java incluso completando los javadoc. Sin embargo yo dejaré aquí el código fuente de la clase Persona sin la documentación para que, quienes no hayan podido programarla puedan igualmente seguir el ejemplo.

Código:
  1. public class Persona {
  2. private String nombre, apellido;
  3. private int edad, documento;
  4. double sueldoPorHora, horasTrabajadas;
  5.  
  6. private Persona(){}
  7.  
  8. public Persona( int documento, String nombre, String apellido, int edad,
  9. double sueldoPorHora, double horasTrabajadas){
  10. this.nombre= nombre;
  11. this.apellido= apellido;
  12. this.edad= edad;
  13. this.documento= documento;
  14. this.sueldoPorHora= sueldoPorHora;
  15. this.horasTrabajadas= horasTrabajadas;
  16. }
  17.  
  18. public int ObtenerDocumentoPersona(){
  19. return this.documento;
  20. }
  21. public String ObtenerNombrePersona(){
  22. return this.nombre;
  23. }
  24.  
  25. public String ObtenerApellidoPersona(){
  26. return this.apellido;
  27. }
  28.  
  29. public int ObtenerEdadPersona(){
  30. return this.edad;
  31. }
  32.  
  33. public double ObtenerSueldoPorHora(){
  34. return this.sueldoPorHora;
  35. }
  36.  
  37. public double ObtenerHorasTrabajadas(){
  38. return this.horasTrabajadas;
  39. }
  40.  
  41. public double CalcularSueldoGanado(){
  42. return this.ObtenerHorasTrabajadas()*this.ObtenerSueldoPorHora();
  43. }
  44.  
  45. public void ModificarNombrePersona(String nombre){
  46. this.nombre= nombre;
  47. }
  48.  
  49. public void ModificarApellidoPersona(String apellido){
  50. this.apellido= apellido;
  51. }
  52.  
  53. public void ModificarDocumentoPersona(int documento){
  54. this.documento= documento;
  55. }
  56.  
  57. public void ModificarEdadPersona(int edad){
  58. this.edad= edad;
  59. }
  60.  
  61. public void ModificarSueldoPorHora(double sueldo){
  62. this.sueldoPorHora= sueldo;
  63. }
  64.  
  65. public void ModificarHorasTrabajadas(double horas){
  66. this.horasTrabajadas= horas;
  67. }
  68.  
  69. public void ImprimirPersona(){
  70. System.out.println("Documento: "+this.documento);
  71. System.out.println("Nombre: "+this.nombre);
  72. System.out.println("Apellido: "+this.apellido);
  73. System.out.println("Edad: "+this.edad);
  74. System.out.println("Sueldo por hora: "+this.sueldoPorHora);
  75. System.out.println("Horas trabajadas: "+this.horasTrabajadas);
  76. }
  77. }



Clase ListaPersonas:

Esta será una primera implementación de listas encadenadas en Java. Como podrán observar a continuación no es algo trivial ya que, un objeto ListaPersonas debe darnos la posibilidad de navegar entre nodos. Eso no es sencillo de hacer en Java por motivos que veremos en detalle. Esta será una implementación particular, luego veremos una más general utilizando herencia que nos dará la posibilidad de tener listas encadenadas de cualquier tipo de objetos.

Recordemos primero la definición del tipo ListaPersonas en Modula:

Código:
  1. TYPE
  2. ListaPersonas= POINTER TO NodoLista;
  3. NodoLista= RECORD
  4. persona: Persona;
  5. siguiente: ListaPersonas;
  6. END;


Como ven tenemos un tipo puntero a un registro que tiene el elemento y luego una variable del tipo puntero. Esta definición recursiva nos funcionaba de maravilla en Pascal y Modula, sin embargo ahora la cuestión cambiará un poco.

Comencemos a ver uno de los problemas que tiene el llevar esta idea tal cual está pensada para Pascal y Modula a Java:

El constructor de nuestro Modulo ListaPersonas era este:

Código:
  1. PROCEDURE CrearListaPersonas(): ListaPersonas;
  2. BEGIN
  3. RETURN NIL;
  4. END CrearListaPersonas


La idea era que dicho constructor inicializara una variable de tipo ListaPersonas asignándole el valor NIL, lo cual representaba para nosotros a una lista vacía. Esto funcionaba a la perfección porque desde fuera del modulo ListaPersonas, dada una variable de ese tipo, no se podía asignar ningún valor a ella, ni siquiera NIL, todo debía hacerse mediante las operaciones definidas en el DEF. Ahora en Java tenemos dos diferencias:

  • Los constructores de Java no pueden llevar una sentencia return porque no tienen tipo de retorno asociado, ni siquiera void.
  • En Java es posible asignar el valor null a una variable de cualquier tipo de objeto sin necesidad de apegarse a las operaciones de su clase.


De este modo no es posible tener un constructor como el de allí arriba ya que para tener un objeto null en Java no hace falta siquiera usar un constructor, basta con una declaración como esta:

Código:
  1. Persona p= null;


Dado este primer problema surge un segundo: ¿Qué modelo le damos entonces a la implementación del tipo? O bien, si le damos el mismo modelo a la implementación del tipo ¿cuál será la mejor manera de representar a una lista vacía?

Claramente estos dos problemas están unidos ya que depende del modelo que yo le dé al tipo que representará a la lista serán las formas de representar una lista vacía que tendré disponibles. En el ejemplo tenemos un tipo ListaPersonas y un tipo NodoListas, ambos relacionados recursivamente. Ahora bien ¿cómo sería en Java? Pues si me declaro una clase ListaPersonas la representación sería así:

Código:
  1. public class ListaPersonas{
  2. private Persona persona;
  3. private ListaPersonas siguiente;
  4. }


Como podrán ver no necesitamos dos tipos, sino que la recursividad se da directa porque tenemos como atributo un objeto de la misma clase que estamos definiendo. Ahora bien, esto parece sencillo, es básicamente el mismo tipo que teníamos en Modula. Nosotros no implementaremos el TAD ListaPersonas con esta representación por varios motivos que ustedes descubrirán ya que como ejercicio les dejaré que implementen ustedes el TAD ListaPersonas con esta representación.

Para lograrlo tienen que tener en cuenta dos cosas:

  • Por lo dicho hace unos momentos, la representación de la lista vacía no puede ser un puntero a null. Tienen que pensar otra forma de saber si una lista es vacía.
  • Como explicaré en breve, no podemos asignar valores a la referencia this, es decir, no podemos hacer this= algo, y eso, aunque no parezca complicará bastante las cosas.


Cita:
El ejercicio entonces constará de lo siguiente:

  • Pensar una representación para una lista vacía.
  • Crear un constructor que cree una lista vacía respetando esa representación.
  • La operación EsVaciaListaPersonas devolverá TRUE o FALSE según la representación elegida parar una lista vacía.
  • Manipular los objetos en Java a fin de lograr insertar al inicio, en un punto intermedio y al final de la lista respetando el orden por documento.
  • No hará falta implementar una operación de destrucción de la lista.
  • Implementar la operación de eliminación de nodos.


Ustedes deberán implementar todo el TAD. Yo listé allí puntos especialmente complejos, no por lo que a listas encadenadas refiere, sino por lo que refiere adaptar su mente a Java implementando algo tan complejo como son las listas.

A continuación veremos la implementación que yo decidí usar. Tal vez les convenga mirarla primero antes de intentar hacer el ejercicio.

¿Por qué les he planteado esto? Porque yo mismo, cuando estaba aprendiendo Java, una de las primeras cosas que quise hacer fue implementar una lista encadenada similar a esta y me encontré con todas las trabas que les mencioné y se me complicó la vida. De verdad aprenderán mucho al hacer este ejercicio.


La idea inicial era entonces pasar el código de Modula a Java lo más parecido posible, es decir que intentaré cambiar prácticamente de una sintaxis a otra. Sin embargo, dada la forma de funcionar de Java respecto a los objetos, los cambios son varios.

Aunque la representación que les dejé a ustedes para trabajar parece la más fiel al tipo original, en realidad no lo es. ¿Por qué? Porque no tenemos ningún tipo NODO, sino que todo es directo. De este modo, si yo me aferraba a dicha representación el código fuente cambiaría sustancialmente, es decir, no solo en aspectos de sintaxis y forma de trabajo de Java, sino también en aspectos lógicos, es decir, cambiaría los algoritmos. Yo quiero dejar exactamente los mismos algoritmos usados en Modula. Para lograrlo tenemos que ver un nuevo aspecto de Java: clases internas.

---------------------------------------------------------------------------------

Clases Internas

Así como una clase puede tener atributos y operaciones, también puede tener otras clases dentro de ella. Estas clases se conocen como clases internas o en inglés inner classes.

Las clases internas son clases propiamente dichas y por tanto definen tipos de objeto y pueden contener todo lo que hemos visto de Java hasta el momento. Por lo general se usan cuando necesitamos definir algún tipo de objeto que solo será usado por una clase y para nada por las demás, por ejemplo, nosotros usaremos una clase interna para definir lo que es un nodo de nuestra lista de personas, por tanto, ninguna otra clase de mi sistema que no sea ListaPersonas necesita conocer un objeto nodo ¿se entiende?

¿Podemos lograr el mismo resultado sin usar clases internas? Por supuesto. Incluso este tema ha sido debatido por los grandes conocedores y ha sido criticado, tanto positivamente como negativamente. Hay gente a favor y en contra de las clases internas. Yo estoy a favor y por tanto les enseñaré a usarlas. Incluso si estuviera en contra debería enseñar a usarlas porque son una herramienta del lenguaje y mi misión aquí es enseñar a usar el lenguaje, me guste o no me guste lo que contenga.

Si nos ponemos a pensar entonces en nuestro problema, un nodo tiene “dentro” una persona y una referencia al siguiente nodo, por tanto, me definiré una clase (que va a ser mi clase interna) llamada NodoLista así:

Código:
  1. class NodoLista{
  2. Persona persona;
  3. NodoLista siguiente;
  4. }


Como ven, esta clase usa el mismo tipo de definición recursiva que les dejé como ejercicio. Ahora bien, tenemos que agregar un constructor a esta clase para poder instanciar objetos de ella. La idea del constructor será generar un nuevo nodo vacío. Yo decidí que un nodo será vacío cuando la referencia persona y la referencia siguiente son ambas null:

Código:
  1. class NodoLista{
  2. Persona persona;
  3. NodoLista siguiente;
  4.  
  5. NodoLista(){
  6. this.persona= null;
  7. this.siguiente= null;
  8. }
  9. }


Todo lindo ¿no? Pero… ¿Dónde declaro una clase interna? Pues sencillamente va dentro de la clase a la que quiere pertenecer, de este modo, nuestra clase ListaPersonas estaría quedando así:

Código:
  1. public class ListaPersonas{
  2.  
  3. private class NodoLista{
  4. Persona persona;
  5. NodoLista siguiente;
  6.  
  7. NodoLista(){
  8. this.persona= null;
  9. this.siguiente= null;
  10. }//Del constructor NodoLista.
  11. }//De la clase interna NodoLista.
  12. }//De la clase ListaPersonas.


Como verán ahora agregué el modificador de acceso private a la declaración de la clase NodoLista. ¿Por qué? Porque si la declaro como interna la idea es que sea privada porque quiero declarar “algo” que solo usaré ahí dentro, de otro modo declararía a la clase NodoLista como una clase común y ya.
Bien, entonces, teniendo la clase NodoLista somos capaces de tener nodos con una persona y un puntero al siguiente nodo. ¿Cómo queda definida la clase ListaPersonas? Pues así:

Código:
  1. public class ListaPersonas{
  2. NodoLista nodo;
  3.  
  4. private class NodoLista{
  5. Persona persona;
  6. NodoLista siguiente;
  7.  
  8. NodoLista(){
  9. this.persona= null;
  10. this.siguiente= null;
  11. }//Del constructor NodoLista.
  12. }//De la clase interna NodoLista.
  13. }//De la clase ListaPersonas.


Observen entonces que la clase ListaPersonas se resume únicamente a tener un puntero a un nodo y ya. Un objeto ListaPersonas entonces será un objeto que contendrá un nodo del tipo NodoLista. Yo podría haber declarado la clase NodoLista como una clase común y tendría el mismo resultado. La diferencia radica básicamente en estos puntos:

  • Al ser interna queda bien claro que los objetos NodoLista son solo usados en ListaPersonas y nada más.
  • No tengo que definir un montón de operaciones para acceder a los atributos de la clase NodoLista, aunque podría hacerlo (sería lo más correcto) por si en algún momento quiero cambiar la forma en que implementé la representación del tipo NodoLista, pero en este ejemplo no lo haremos.
  • Todo forma parte de un mismo TAD lo cual deja todo mucho más claro para otros programadores. Esto refiere a lo mismo que el primer punto mencionado


El resto de código de la clase ListaPeronas puede ir tanto sobre la declaración de la clase interna como por debajo. Normalmente se suele dejar la declaración de la clase interna y su código fuente debajo del todo.

NOTAS: Al crear una clase normal, es decir, una clase en su propio archivo, quedará guardada con extensión .java. Lo mismo si dentro de una clase declaramos clases internas. En este caso tendremos un archivo ListaPersonas.java. Ahora bien, cuando compilamos, por cada clase Java genera un archivo .class con el nombre de la clase. Sin embargo, cuando una clase tiene clases internas, Java generará un archivo que contendrá el nombre de la clase global (externa), un signo de $ y luego el nombre de la clase interna. Por ejemplo, como ListaPersonas tiene una clase interna tendremos

  • ListaPersonas.class
  • ListaPersonas$NodoLista.class

Otro punto importante es que podemos, a pesar de la idea original, declarar una clase interna como pública de modo que podamos usar objetos de ella en otras clases. No tiene mucho sentido pero se puede hacer. Si yo quisiera tener objetos NodoLista en otra clase que no fuera mi clase ListaPersonas primero tendría que declarar la clase NodoLista como pública. Con eso basta. Dado que la clase interna justamente es interna a otra, debo acceder a ella por medio de la clase que la contiene, en este ejemplo podría crear un objeto NodoLista así:

Código:
  1. ListaPersonas.NodoLista miNodo= new ListaPersonas.NodoLista();


No me extenderé en este aspecto porque no me parece el más adecuado. Ustedes pueden ahondar en él si quieren, pero no veremos ejemplos de usos de una clase interna como si fuera externa en este tutorial.

-------------------------------------------------------------------------------------

Operaciones de la clase ListaPersonas:

Bien, dada la representación anterior de la clase ListaPersonas veamos algunos aspectos importantes a la hora de pasar el código de Modula a Java. Comencemos con el constructor, cuyo código Modula era:

Código:
  1. PROCEDURE CrearListaPersonas(): ListaPersonas;
  2. BEGIN
  3. RETURN NIL;
  4. END CrearListaPersonas;


Bien, ahora en Java y con la representación que elegí será así:

Código:
  1. public ListaPersonas(){
  2. this.nodo= null;
  3. }


Si el objeto nodo es nulo entonces significará para nosotros que la lista es vacía, en caso contrario tendremos por lo menos un nodo válido y por tanto no será vacía.

La otra operación constructora era AgregarPersona, cuyo código en Modula (sin comentarios) era:

Código:
  1. PROCEDURE AgregarPersona(p: Persona; VAR l: ListaPersonas);
  2. VAR
  3. iter1, iter2: ListaPersonas;
  4. posicionEncontrada: BOOLEAN;
  5. BEGIN
  6. IF EsVaciaListaPersonas(l) THEN
  7. NEW(l);
  8. l^.persona:= p;
  9. l^.siguiente:= NIL;
  10. ELSE
  11. posicionEncontrada:= FALSE;
  12.  
  13. IF(ObtenerDocumentoPersona(l^.persona)>ObtenerDocumentoPersona(p))THEN
  14. NEW(iter1);
  15. iter1^.persona:= p;
  16. iter1^.siguiente:= l;
  17. l:= iter1;
  18. ELSE
  19. iter1:= l;
  20. WHILE (iter1<>NIL) AND (NOT posicionEncontrada) DO
  21. posicionEncontrada:= ObtenerDocumentoPersona(p) <
  22. ObtenerDocumentoPersona(iter1^.persona);
  23. IF NOT posicionEncontrada THEN
  24. iter2:= iter1;
  25. iter1:= iter1^.siguiente;
  26. END;
  27. END;
  28.  
  29. IF (posicionEncontrada) THEN
  30. NEW(iter2^.siguiente);
  31. iter2:= iter2^.siguiente;
  32. iter2^.persona:= p;
  33. iter2^.siguiente:= iter1;
  34. ELSE
  35. NEW(iter2^.siguiente);
  36. iter2:= iter2^.siguiente;
  37. iter2^.persona:= p;
  38. iter2^.siguiente:= NIL;
  39. END;
  40. END;
  41. END;
  42. END AgregarPersona;


Básicamente en este código nosotros iterábamos sobre la lista hasta encontrar la posición en la que debía ir insertada la nueva persona según su documento. Para tal taréa teníamos declarados iteradores llamados iter1 e iter2 que eran de tipo ListaPersonas. En la representación que diseñé ahora en Java, los iteradores ya no son del tipo ListaPersonas sino que son del tipo NodoLista porque ahora los nodos de nuestra lista son justamente objetos NodoLista y no como en Modula que eran objetos ListaPersona. Como dije antes, la recursividad del tipo ya no está en ListaPersona sino en NodoLista.
El por qué de esta representación comenzará a hacerse claro ahora, viendo poco a poco el código en Modula y cómo queda en Java. Veamos las primeras líenas de esta operación:

Código:
  1. PROCEDURE AgregarPersona(p: Persona; VAR l: ListaPersonas);
  2. VAR
  3. iter1, iter2: ListaPersonas;
  4. posicionEncontrada: BOOLEAN;
  5. BEGIN
  6. IF EsVaciaListaPersonas(l) THEN
  7. NEW(l);
  8. l^.persona:= p;
  9. l^.siguiente:= NIL;
  10. ELSE


En Java queda así:

Código:
  1. public void AgregarPersona(Persona p){
  2. NodoLista iter1, iter2 = null;
  3. boolean posicionEncontrada;
  4.  
  5. if(this.EsVaciaListaPersonas()){
  6. this.nodo= new NodoLista();
  7. this.nodo.persona= p;
  8. this.nodo.siguiente= null;
  9. }else{


Es casi idéntico salvo que lo que antes era nuestro parámetro por referencia (la lista a modificar) llamado l, ahora pasa a ser this.nodo. Salvo eso, todo es igual, incluso las líneas de código (excepto por el BEGIN inicial en Modula). Teniendo eso en cuenta, el código completo quedaría:

Código:
  1. public void AgregarPersona(Persona p){
  2. NodoLista iter1, iter2 = null;
  3. boolean posicionEncontrada;
  4.  
  5. if(this.EsVaciaListaPersonas()){
  6. this.nodo= new NodoLista();
  7. this.nodo.persona= p;
  8. this.nodo.siguiente= null;
  9. }else{
  10. posicionEncontrada= false;
  11.  
  12. if(this.nodo.persona.ObtenerDocumentoPersona()>p.ObtenerDocumentoPersona()){
  13. iter1= new NodoLista();
  14. iter1.persona= p;
  15. iter1.siguiente= this.nodo;
  16. this.nodo= iter1;
  17. }else{
  18. iter1= this.nodo;
  19. while((iter1!=null)&&(!posicionEncontrada)){
  20. posicionEncontrada= p.ObtenerDocumentoPersona()<
  21. iter1.persona.ObtenerDocumentoPersona();
  22. if(!posicionEncontrada){
  23. iter2= iter1;
  24. iter1= iter1.siguiente;
  25. }
  26. }
  27.  
  28. if(posicionEncontrada){
  29. iter2.siguiente= new NodoLista();
  30. iter2= iter2.siguiente;
  31. iter2.persona= p;
  32. iter2.siguiente= iter1;
  33. }else{
  34. iter2.siguiente= new NodoLista();
  35. iter2= iter2.siguiente;
  36. iter2.persona= p;
  37. iter2.siguiente= null;
  38. }
  39. }
  40. }
  41. }


Si ustedes hacen una comparación línea a línea entre el código de Modula y el de Java verán que son lo mismo. Eso era lo que yo quería lograr, el mismo código en ambos lenguajes a fin de no variar en absoluto el algoritmo y la lógica usados. Si han escrito el código con la representación que les pedí como ejercicio notarán muchísimas diferencias.

Veamos el código completo en Modula y luego en Java. La comparacíon línea por línea les queda a ustedes como ejercicio. Quitaré los comentarios para simplificar el código; además, esto ya lo trabajamos en detalle en Modula, por tanto las dudas algorítmicas repásenlas.

Modulo ListaPersonas:

Código:
  1. IMPLEMENTATION MODULE ListaPersonas;
  2.  
  3. FROM Persona IMPORT Persona, ObtenerNombrePersona, ObtenerDocumentoPersona,
  4. ObtenerApellidoPersona, DestruirPersona;
  5. FROM Storage IMPORT ALLOCATE, DEALLOCATE;
  6. FROM SWholeIO IMPORT WriteCard;
  7. FROM STextIO IMPORT WriteString, WriteLn;
  8.  
  9. TYPE
  10. ListaPersonas= POINTER TO NodoLista;
  11. NodoLista= RECORD
  12. persona: Persona;
  13. siguiente: ListaPersonas;
  14. END;
  15.  
  16. PROCEDURE CrearListaPersonas(): ListaPersonas;
  17. BEGIN
  18. RETURN NIL;
  19. END CrearListaPersonas;
  20.  
  21. PROCEDURE AgregarPersona(p: Persona; VAR l: ListaPersonas);
  22. VAR
  23. iter1, iter2: ListaPersonas;
  24. posicionEncontrada: BOOLEAN;
  25. BEGIN
  26. IF EsVaciaListaPersonas(l) THEN
  27. NEW(l);
  28. l^.persona:= p;
  29. l^.siguiente:= NIL;
  30. ELSE
  31. posicionEncontrada:= FALSE;
  32.  
  33. IF (ObtenerDocumentoPersona(l^.persona)>ObtenerDocumentoPersona(p)) THEN
  34. NEW(iter1);
  35. iter1^.persona:= p;
  36. iter1^.siguiente:= l;
  37. l:= iter1;
  38. ELSE
  39. iter1:= l;
  40. WHILE (iter1<>NIL) AND (NOT posicionEncontrada) DO
  41. posicionEncontrada:= ObtenerDocumentoPersona(p) <
  42. ObtenerDocumentoPersona(iter1^.persona);
  43. IF NOT posicionEncontrada THEN
  44. iter2:= iter1;
  45. iter1:= iter1^.siguiente;
  46. END;
  47. END;
  48.  
  49. IF (posicionEncontrada) THEN
  50. NEW(iter2^.siguiente);
  51. iter2:= iter2^.siguiente;
  52. iter2^.persona:= p;
  53. iter2^.siguiente:= iter1;
  54. ELSE
  55. NEW(iter2^.siguiente);
  56. iter2:= iter2^.siguiente;
  57. iter2^.persona:= p;
  58. iter2^.siguiente:= NIL;
  59. END;
  60. END;
  61. END;
  62. END AgregarPersona;
  63.  
  64. PROCEDURE ExistePersona(documento: CARDINAL; l: ListaPersonas): BOOLEAN;
  65. VAR iter: ListaPersonas;
  66. BEGIN
  67. iter:= l;
  68. WHILE (NOT EsVaciaListaPersonas(iter)) AND
  69. (ObtenerDocumentoPersona(iter^.persona)<>documento) DO
  70. iter:= iter^.siguiente;
  71. END;
  72.  
  73. RETURN NOT EsVaciaListaPersonas(iter);
  74. END ExistePersona;
  75.  
  76. PROCEDURE EsVaciaListaPersonas(l: ListaPersonas): BOOLEAN;
  77. BEGIN
  78. RETURN (l=NIL);
  79. END EsVaciaListaPersonas;
  80.  
  81. PROCEDURE EsLlenaListaPersonas(l: ListaPersonas): BOOLEAN;
  82. BEGIN
  83. RETURN FALSE;
  84. END EsLlenaListaPersonas;
  85.  
  86. PROCEDURE ObtenerPersona(documento: CARDINAL; l: ListaPersonas): Persona;
  87. VAR iter: ListaPersonas;
  88. BEGIN
  89. iter:= l;
  90. WHILE (ObtenerDocumentoPersona(iter^.persona)<>documento) DO
  91. iter:= iter^.siguiente;
  92. END;
  93. RETURN iter^.persona;
  94. END ObtenerPersona;
  95.  
  96. PROCEDURE ListarListaPersonas(l: ListaPersonas);
  97. VAR iter: ListaPersonas;
  98. BEGIN
  99. iter:= l;
  100. REPEAT
  101. WriteCard(ObtenerDocumentoPersona(iter^.persona),1); WriteString(" ");
  102. WriteString(ObtenerNombrePersona(iter^.persona)); WriteString(" ");
  103. WriteString(ObtenerApellidoPersona(iter^.persona)); WriteString(" ");
  104. WriteLn;
  105. iter:= iter^.siguiente;
  106. UNTIL (EsVaciaListaPersonas(iter));
  107. END ListarListaPersonas;
  108.  
  109. PROCEDURE EliminarPersona(documento: CARDINAL; VAR l: ListaPersonas);
  110. VAR
  111. iter, aux: ListaPersonas;
  112. BEGIN
  113. IF NOT EsVaciaListaPersonas(l) THEN
  114. iter:= l;
  115.  
  116. IF (ObtenerDocumentoPersona(iter^.persona)=documento) THEN
  117. iter:= l;
  118. l:= l^.siguiente;
  119. DestruirPersona(iter^.persona);
  120. DISPOSE(iter);
  121. ELSE
  122. WHILE(ObtenerDocumentoPersona(iter^.siguiente^.persona)<>documento)DO
  123. iter:= iter^.siguiente;
  124. END;
  125. aux:= iter^.siguiente;
  126. iter^.siguiente:= aux^.siguiente;
  127. DestruirPersona(aux^.persona);
  128. DISPOSE(aux);
  129. END;
  130. END;
  131. END EliminarPersona;
  132.  
  133. PROCEDURE DestruirListaPersonas(VAR l: ListaPersonas);
  134. VAR
  135. iter, aux: ListaPersonas;
  136. BEGIN
  137. iter:= l;
  138. WHILE (NOT EsVaciaListaPersonas(iter)) DO
  139. aux:= iter;
  140. iter:= iter^.siguiente;
  141. DestruirPersona(aux^.persona);
  142. DISPOSE(aux);
  143. END;
  144. END DestruirListaPersonas;
  145. END ListaPersonas.


Clase ListaPersonas:

Código:
  1. public class ListaPersonas {
  2. NodoLista nodo;
  3.  
  4. public ListaPersonas(){
  5. this.nodo= null;
  6. }
  7.  
  8. public void AgregarPersona(Persona p){
  9. NodoLista iter1, iter2 = null;
  10. boolean posicionEncontrada;
  11.  
  12. if(this.EsVaciaListaPersonas()){
  13. this.nodo= new NodoLista();
  14. this.nodo.persona= p;
  15. this.nodo.siguiente= null;
  16. }else{
  17. posicionEncontrada= false;
  18.  
  19. if(this.nodo.persona.ObtenerDocumentoPersona()>p.ObtenerDocumentoPersona()){
  20. iter1= new NodoLista();
  21. iter1.persona= p;
  22. iter1.siguiente= this.nodo;
  23. this.nodo= iter1;
  24. }else{
  25. iter1= this.nodo;
  26. while((iter1!=null)&&(!posicionEncontrada)){
  27. posicionEncontrada= p.ObtenerDocumentoPersona()<iter1.persona.ObtenerDocumentoPersona();
  28. if(!posicionEncontrada){
  29. iter2= iter1;
  30. iter1= iter1.siguiente;
  31. }
  32. }
  33.  
  34. if(posicionEncontrada){
  35. iter2.siguiente= new NodoLista();
  36. iter2= iter2.siguiente;
  37. iter2.persona= p;
  38. iter2.siguiente= iter1;
  39. }else{
  40. iter2.siguiente= new NodoLista();
  41. iter2= iter2.siguiente;
  42. iter2.persona= p;
  43. iter2.siguiente= null;
  44. }
  45. }
  46. }
  47. }
  48.  
  49. public boolean ExistePersona(int documento){
  50. NodoLista iter;
  51.  
  52. iter= this.nodo;
  53. while((iter!=null)&&(iter.persona.ObtenerDocumentoPersona()!=documento)){
  54. iter= iter.siguiente;
  55. }
  56.  
  57. return !(iter==null);
  58. }
  59.  
  60. public boolean EsVaciaListaPersonas(){
  61. return this.nodo==null;
  62. }
  63.  
  64. public Persona ObtenerPersona(int documento){
  65. NodoLista iter;
  66.  
  67. iter= this.nodo;
  68. while (iter.persona.ObtenerDocumentoPersona()!=documento){
  69. iter= iter.siguiente;
  70. }
  71. return iter.persona;
  72. }
  73.  
  74. public void ListarListaPersonas(){
  75. NodoLista iter;
  76.  
  77. iter= this.nodo;
  78. do{
  79. System.out.println(iter.persona.ObtenerDocumentoPersona()+" "+
  80. iter.persona.ObtenerNombrePersona()+" "+
  81. iter.persona.ObtenerApellidoPersona()+" ");
  82. iter= iter.siguiente;
  83. }while(iter!=null);
  84. }
  85.  
  86. public void EliminarPersona(int documento){
  87. NodoLista iter, aux;
  88.  
  89. if (!this.EsVaciaListaPersonas()){
  90. iter= this.nodo;
  91.  
  92. if(iter.persona.ObtenerDocumentoPersona()==documento){
  93. iter= this.nodo;
  94. this.nodo= this.nodo.siguiente;
  95. iter.persona= null;
  96. iter= null;
  97. }else{
  98. while(iter.siguiente.persona.ObtenerDocumentoPersona()!=documento){
  99. iter= iter.siguiente;
  100. }
  101. aux= iter.siguiente;
  102. iter.siguiente= aux.siguiente;
  103. aux.persona= null;
  104. aux= null;
  105. }
  106. }
  107. }
  108.  
  109.  
  110. private class NodoLista{
  111. Persona persona;
  112. NodoLista siguiente;
  113.  
  114. NodoLista(){
  115. this.persona= null;
  116. this.siguiente= null;
  117. }
  118. }
  119. }


==============================

En la próxima lección veremos la clase principal y algunos otros aspectos de Java y NetBeans.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
pirdy
Usuario Inquieto


Registrado: 11 Feb 2010
Mensajes: 137

Mensaje Publicado: Jueves 01 Nov 2012 00:57

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

Muchas gracias profe por volver a publicar otra leccion.

Espero algun dia llegar a estar por estas lecciones ya que sería señal de que puedo programar a un nivel optimo.

Saludos.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Sebastián Marcos
Usuario Inquieto


Registrado: 04 Oct 2011
Mensajes: 133

Mensaje Publicado: Jueves 01 Nov 2012 05:15

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

Muchas gracias por la lección Kyshuo.
Es una gran coincidencia, pero estaba intentando implementar las listas enlazadas en Java. Algo que en principio pensé que sería fácil de realizar se me había complicado bastante, justamente por la forma de trabajar de este lenguaje.
Excelente como siempre.
Saludos.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
sr floyd
Usuario Activo


Registrado: 05 Sep 2011
Mensajes: 196

Mensaje Publicado: Jueves 01 Nov 2012 14:00

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

Excelente trabajo el del compañero en Lazarus!! Me gustaría aprender al respecto. Gracias por compartirlo Kyshuo.

Volver arriba
Ver perfil del usuario Enviar mensaje privado MSN Messenger
Sebastián Marcos
Usuario Inquieto


Registrado: 04 Oct 2011
Mensajes: 133

Mensaje Publicado: Sábado 03 Nov 2012 17:48

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

Hola a todos!!!.
Estimado Kyshuo, respecto al ejercicio de listas enlazadas en Java, en relación al punto:
Cita:
Pensar una representación para una lista vacía


En particular, ¿debemos ajustarnos a la representación?:
Código:
public class ListaPersonas{
   private Persona persona;
   private ListaPersonas siguiente;
}

Porque pensé en agregar un atributo que contara el número de nodos de la lista o lo que es lo mismo su largo. De este modo si largo == 0, esto me representaría la lista vacía. No sé, tal vez me esté apresurando...
Saludos.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Kyshuo Ayame
Moderador Global


Registrado: 07 Ene 2011
Mensajes: 1044

Mensaje Publicado: Sábado 03 Nov 2012 17:59

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

El planteo va por el lado de acoplarse a esa representación sin agregar atributos extra. En particular amigo, creo realmente que del modo en que lo dices estarías complicándote la vida un poco, pero podrías hacerlo (tendrá sus impicancias respecto al atributo que lleve la cuenta pero seguro ya las pensaste).

Mi consejo sería que lo hagas de ambas maneras, primero sin el atributo y luego modificándolo para agregarlo (seguro la modificación sería muy poca).

Saludos.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Sebastián Marcos
Usuario Inquieto


Registrado: 04 Oct 2011
Mensajes: 133

Mensaje Publicado: Domingo 04 Nov 2012 01:36

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

Entendido Kyshuo.
Siguiendo el desarrollo de la lección, y en particular la representación que has realizado para la clase interna NodoLista que la tomo de referencia, si la representación de una lista en el ejercicio está dada por:
Código:
public class ListaPersonas{
   private Persona persona;
   private ListaPersonas siguiente;
}

Propondría que la lista es vacía cuando las referencias persona y siguiente son null. De este modo el constructor que crea una lista vacía estaría dado por:
Código:
//Crea una lista vacía según la representación anterior.
ListaPersonas(){
  this.persona = null;
  this.siguiente = null;
}


La operación es EsVaciaListaPersonas la definiría así:

Código:
//Predicado que retorna true si la lista es vacía o false en caso contrario.
public boolean EsVaciaListaPersonas(){
   return (this.persona == null)  && (this.siguiente == null)
}


No sé si estoy encaminado...
Saludos.

Agrego:
1) Una consulta aparte, no tenemos en Java nombres de operaciones modificadoras o selectoras predefinidos. Por ejemplo, en lugar de tener la operación ObtenerDocumentoPersona a la cual nosotros le damos nombre, no hay alguna como GetDocumento que haga referencia directamente al atributo documento.

2) Respecto a la representación que has utilizado para la clase ListaPersonas análoga a la de Modula, si el contenido de la clase interna NodoLista fuera de un tipo primitivo, por ejemplo un entero, y no una referencia a un objeto, por ejemplo:
Código:
private class NodoLista{
               int numero;
               NodoLista siguiente;
}

¿Cómo sería el constructor para NodoLista en este caso? Pues aquí no es válido hacer this.numero = null ya que numero no es una referencia a un objeto.
Agrego: Me contesto a esto último en parte, creo que this.numero se puede inicializar con cualquier valor (o por defecto en cero), y esto no afectaría al instanciar objetos de NodoLista, pues al manejar referencias a objetos de este tipo, lo importante para crear un nodo vacío es que el atributo siguiente sea nulo, no importando los guardado en principio en el atributo número (Espero se entienda lo que digo...jeje).

Perdón por la avalancha de preguntas, es que me van surgiendo a medida que leo la lección.
Saludos.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Kyshuo Ayame
Moderador Global


Registrado: 07 Ene 2011
Mensajes: 1044

Mensaje Publicado: Domingo 04 Nov 2012 15:19

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

Bien amigo, te contesto cada uno de tus excelentes planteos:

Cita:
Propondría que la lista es vacía cuando las referencias persona y siguiente son null. De este modo el constructor que crea una lista vacía estaría dado por:

Código:
  1. //Crea una lista vacía según la representación anterior.
  2. ListaPersonas(){
  3. this.persona = null;
  4. this.siguiente = null;
  5. }



La operación es EsVaciaListaPersonas la definiría así:

Código:
  1. //Predicado que retorna true si la lista es vacía o false en caso contrario.
  2. public boolean EsVaciaListaPersonas(){
  3. return (this.persona == null) && (this.siguiente == null)
  4. }


Estas bien encaminado o, mejor dicho, es el camino correcto. Incluso podrías acotar la condición de lista vacía a que la referencia persona sea null ya que en tal caso por razones obvias no existiría un siguiente. Claro está que esa parte la controlarías tú al programar el resto de las operaciones. Esto es solo una posible opción, la que elegiste está super correcta. Siempre tenemos más de un camino para hacer lo mismo.

Cita:
1) Una consulta aparte, no tenemos en Java nombres de operaciones modificadoras o selectoras predefinidos. Por ejemplo, en lugar de tener la operación ObtenerDocumentoPersona a la cual nosotros le damos nombre, no hay alguna como GetDocumento que haga referencia directamente al atributo documento.


En realidad, y esto lo veremos luego, las operaciones OBTENER y ASIGNAR se nombran por convención como GET y SET (conocidas como GETTER y SETTER), por tanto como tu bien dices tendríamos una operación getDocumento. Sin embargo no termino de entender qué quieres decir con eso de "que haga referencia directamente al atributo documento". En realidad su código sería

Código:
  1. public getDocumento(){
  2. return this.documento;
  3. }


Suponiendo claro que el documento lo declaramos como entero.

Luego veremos una herramienta de NetBeans que, declarados los atributos de una clase, nos genera automáticamente todas las operaciones GETTER y SETTER para ellos, incluso los constructores. Con eso, las clases simples, se resumen prácticamente a escribir sus atributos, NetBeans hace el resto.

No lo he dado aún porque quiero que los lectores aprendan Java y no se descansen en las herramientas automatizadas del IDE.

Respecto a tu última pregunta pues está bien. Digamos que si vas a construir un nodo en esas condiciones asumimos que no construirás nodos vacíos ya que el atributo numero siempre tendrá un valor con el que lo inicializarás o 0 si no lo haces tú (Java inicializa en 0 a los números). De este modo un nodo siempre tendrá información y por tanto no puede ser vacío. El control de esto lo harías pues desde la clase principal. Si esta no tiene nodos es vacía, si tiene al menos uno no lo es.

Espero se entienda la idea.

Saludos amigo.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Sebastián Marcos
Usuario Inquieto


Registrado: 04 Oct 2011
Mensajes: 133

Mensaje Publicado: Domingo 04 Nov 2012 18:21

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

Muchas gracias Kyshuo por tus respuestas.
Respecto a:
Cita:
Sin embargo no termino de entender qué quieres decir con eso de "que haga referencia directamente al atributo documento".

No ha sido feliz mi redacción, pero de igual forma me has contestado excelentemente como siempre.
Saludos.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Sebastián Marcos
Usuario Inquieto


Registrado: 04 Oct 2011
Mensajes: 133

Mensaje Publicado: Miércoles 07 Nov 2012 17:59

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

Hola a todos.
Bueno, me he fijado el objetivo de implementar distintas operaciones sobre listas enlazadas simples en Java, usando la representación similar a la de Modula (con la cual se trabaja en la clase ListaPersonas).
La diferencia es que me he creado una clase que represente una lista de enteros y no de objetos Persona como en la lección, sólo para facilitar un poco el trabajo, y con el objetivo de afianzar el manejo de este tipo de estructuras en Java.

La representación es esta:
Código:
public class Lista {
    NodoLista nodoPrimero; //La clase lista se define sólo como un puntero a un nodo (el primero).
   
   //La clase NodoLista la definimos como una clase interna a la clase lista.
    private class NodoLista{
        int numero;
        NodoLista siguiente;  //Definición recursiva de NodoLista.
       
        //Constructor de NodoLista.
        NodoLista(){
            this.numero= 0;
            this.siguiente= null;
        }//Del constructor NodoLista.
    }//De la clase interna NodoLista.

Como se puede ver es una definición idéntica a la de la lección, salvo que los nodos contienen enteros y no objetos Persona.

Pues, he implementado varias operaciones con esta definición de forma iterativa y me han funcionado bien. Por ejemplo insertarAlPrincipio, insertarAlFinal, buscarElemento, buscarMayorElemento, etc, etc.
Por ejemplo, la operación insertarAlfinal:
Código:
 public void insertarAlFinal(int n){
        NodoLista iter;  //Para iterar en la lista.
        NodoLista aux= new NodoLista();  //Para agregar al final de la lista.
       
        aux.numero=n;   //Cargamos la nueva celda.
        aux.siguiente=null;
       
        //Si la lista es vacía agregamos directemente.
        if (this.nodoPrimero== null){
            this.nodoPrimero= aux;
        }else{    //iteramos en la lista hasta el final.
            iter= this.nodoPrimero;
            while(iter.siguiente!= null){
                iter= iter.siguiente;
            }
            //Engancho aux a continuación del último.
            iter.siguiente= aux;
           
            }
        }

Un código casí idéntico al que se podría realizar en Modula.

El problema se me presenta cuando quiero implementar los métodos de forma recursiva. Por ejemplo éste último (insertarAlFinal), no me doy cuenta cómo realizarlo en Java, en parte porque ahora la referencia a la lista (al primer nodo) se pasa con el parámetro implícito this.
Desde ya muchas gracias y saludos a todos.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Kyshuo Ayame
Moderador Global


Registrado: 07 Ene 2011
Mensajes: 1044

Mensaje Publicado: Miércoles 07 Nov 2012 20:24

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

Excelente pregunta amigo y excelente tragajo e iniciativa, así es que se aprende. Vamos al tema en cuestión. Resulta difícil adaptar la cabeza a esto de la referencia implícita this y al hecho de que Java pasa los parámetros siempre por valor. Una forma que a mí me ha funcionado bastante a la hora de "cambiar de chip" entre una forma de trabajo y la otra es mirar cómo está definida la operación en Modula y luego cambiar el pasaje del parámetro recursivo por un this. Tu operación es esta:

Código:
  1. public void insertarAlFinal(int n){
  2. NodoLista iter; //Para iterar en la lista.
  3. NodoLista aux= new NodoLista(); //Para agregar al final de la lista.
  4.  
  5. aux.numero=n; //Cargamos la nueva celda.
  6. aux.siguiente=null;
  7.  
  8. //Si la lista es vacía agregamos directemente.
  9. if (this.nodoPrimero== null){
  10. this.nodoPrimero= aux;
  11. }else{ //iteramos en la lista hasta el final.
  12. iter= this.nodoPrimero;
  13. while(iter.siguiente!= null){
  14. iter= iter.siguiente;
  15. }
  16. //Engancho aux a continuación del último.
  17. iter.siguiente= aux;
  18.  
  19. }
  20. }


Adaptemos un poco esta operación para que sea recursiva. En Moudula me habría definido un parámetro por referencia que reciba algo del tipo Lista, sin embargo en esta representación hay que tener en cuenta:

  • La representación que elgiste hace que iteres sobre objetos NodoLista y no sobre objetos Lista, por tanto la recursividad hará lo mismo, es decir, se hará sobre NodoLista.
  • Dado lo anterior lo mejor sería que la operación recursiva esté declarada dentro la clae NodoLista y, desde la operación insertarAlFinal únicamente invocas a la operación de NodoLista.


Por esas condiciones surje la necesidad de que la recursividad la realice otra operación y que insertarAlFinal la invoque. ¿Por qué? Pues porque insertarAlFinal es una operación de instancia de la clase Lista y por tanto solo puede invocarse mediante objetos Lista y por tanto su referencia this será de tipo Lista. Si la representación fuera como la dejada como ejercicio en esta lección no habría problema, pero dada esta representación necesitamos entonces una operación que reciba NodoLista.

Opciones para esto: Se me ocurren dos, lo cual no implica que no existan más:

  • Como ya dije, declaramos la operación dentro de la clase NodoLista.
  • Declaramos una operación estática dentro de la clase Lista la cual reciba un número como argumento, además del número algo del tipo NodoLista.


Para la segunda opción tendríamos una firma como esta:

Código:
  1. private static void insertarAlFinal(int n, NodoLista nodo)


De este modo obtienes una operación que trabja casi exactamente como lo hacíamos en Modula. Dado que es estática no existe referencia this aplicable a ella. Te dejo a tí la implementación.

La primera opción debe ser entonces implementada dentro de NodoLista de modo que en ese contexto this sea algo de ese tipo. Como verás Java se va complicando, al menos hasta tener bien claro esto de this, static y demás.

Lo primero que tenemos que tener en cuenta es que por la representación no tenemos forma de saber si un nodo es vacío porque en este contexto eso no existe, un nodo siempre tendrá un número y por tanto si el nodo existe no es vacío. De este modo, la recursividad se iniciará siempre con una lista que tenga al menos un nodo válido. Así, si la lista es vacía la cuestión se resolverá diréctamente. Eso lo veremos en detalle. Primero veamos la operación de NodoLista que es recursiva y que asumimos iniciará con un nodo válido. Agregaré además un constructor a NodoLista que me permita crear un nodo ya con un número.

Veamos el código completo de la clase NodoLista. Como podrás apreciar solo agregué un constructor y la operación insertarAlFinal.

Código:
  1. public class NodoLista{
  2. int numero;
  3. NodoLista siguiente;
  4.  
  5. NodoLista(){
  6. this.numero= 0;
  7. this.siguiente= null;
  8. }
  9.  
  10. NodoLista(int numero){
  11. this.numero= numero;
  12. this.siguiente= null;
  13. }
  14.  
  15. public void insertarAlFinal(int n){
  16. if (this.siguiente== null){
  17. this.siguiente= new NodoLista(n);
  18. }else{ //iteramos en la lista hasta el final.
  19. this.siguiente.insertarAlFinal(n);
  20. }
  21. }
  22. }


El código es claramente muchísimo más corto que la iteración.
En vez de tener un parámetro por referencia al cual le paso algo como iter.siguiente, hago el llamado a la operación con el siguiente nodo del actual. Esto es algo para que analices, porque la recursividad está justamente en el llamado this.siguiente.insertarAlFinal(n).

¿Por qué? Pues en cada nueva entrada a la operación la referncia this es otra ¿me sigues? Por tanto vamos avanzando hasta que estamos en un nodo cuyo siguiente sea nulo. En ese caso creo el nuevo nodo diréctamente allí y listo.

¿Por qué voy mirando siempre el siguiente y no el nodo actual? Porque si yo detengo la recursividad en un nodo que es null me habré salido de la lista. Además, si this==null no puedo hacer algo como this= new ....

Bien ¿cómo queda la operación insertarAlFinal de la clase Lista?

Código:
  1. public void insertarAlFinal(int n){
  2. if(this.nodoPrimero==null){
  3. this.nodoPrimero= new NodoLista(n);
  4. }else{
  5. this.nodoPrimero.insertarAlFinal(n);
  6. }
  7. }


Bien, la primera condición es igual a la que tú tenías. Si el nodo no es vacío entonces hago el llamado a la operación recursiva mediante el nodo. Ten en cuenta que, aunque la firma de las operaciones es idéntica, el llamado se hace sobre un objeto NodoLista y por tanto se está llamando a aquella operación y no a la que estamos implementando.

Cualquier duda me preguntas amigo.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Sebastián Marcos
Usuario Inquieto


Registrado: 04 Oct 2011
Mensajes: 133

Mensaje Publicado: Jueves 08 Nov 2012 04:47

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

Gracias Kyshuo, una explicación y respuesta digna de un experto.
Estas posibilidades para abordar el problema:
Cita:
Opciones para esto: Se me ocurren dos, lo cual no implica que no existan más:

Como ya dije, declaramos la operación dentro de la clase NodoLista.
Declaramos una operación estática dentro de la clase Lista la cual reciba un número como argumento, además del número algo del tipo NodoLista.

son geniales, así como la forma que describes para implementar la primera.

Apenas tenga tiempo, seguiré realizando pruebas y cualquier cosa comentaré.
Saludos y gracias nuevamente.

Agrego: Un diagrama que he utilizado para visualizar y trabajar con listas enlazadas de enteros en Java es el siguiente:
https://plus.google.com/photos/103464529387593351859/albums/5461875844333928561/5808306441310879154?authkey=COPG2q3v_Pj9vQE

Saludos.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Kyshuo Ayame
Moderador Global


Registrado: 07 Ene 2011
Mensajes: 1044

Mensaje Publicado: Jueves 08 Nov 2012 16:08

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

Excelente el diagrama amigo. Más adelante veremos algunos aspectos de Java que hacen que las colecciones (Listas, Pilas, Colas, Arboles, etc) se implementen de forma "interna" a la clase. Esto implica otro "cambio de chip" en nuestra cabeza dada la forma en que aprendimos a programar. Obviamente eso tendrá sus ventajas y desventajas y tranquilamente podemos en Java programar las colecciones del mismo modo que hacíamos en Modula o Pascal.

Saludos amigo.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Sebastián Marcos
Usuario Inquieto


Registrado: 04 Oct 2011
Mensajes: 133

Mensaje Publicado: Sábado 10 Nov 2012 03:26

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

Hola a todos!!!
Estimado Kyshuo, siguiendo una de tus propuestas para implementar de forma recursiva los métodos para trabajar con listas enlazadas de enteros, precisamente la de definirse en la clase NodoLista la operación recursiva sobre objetos de esta clase (ya que estos objetos son los definidos recursivamente), y luego invocar a la misma
desde su homóloga en la clase principal (exterior) Lista, debo decir que todo a funcionado de maravilla. He logrado implementar la versión recursiva de varios de estos métodos básicos para el manejo de listas, habiendo seguido tu ejemplo con el método insertarAlFinal.

Ahora bien, tu me propusiste otra opción, la recuerdo:
Cita:
Declaramos una operación estática dentro de la clase Lista la cual reciba un número como argumento, además del número algo del tipo NodoLista.


Además me facilitaste la firma de la operación (muy parecida a la que se podría utilizar en Modula):
Código:
private static void insertarAlFinal(int n, NodoLista nodo)


Esta operación recibe un entero y una referencia del tipo NodoLista.
Bien, el problema es que este método es estático (de clase), y este tipo de métodos no pueden acceder a variables no estáticas (de instancia), entonces, ¿debería declarar los atributos de la clase NodoLista como estáticos?, y por consiguiente, ¿cambiar la representación que tengo de dicha clase?.
En métodos estáticos uno usa la referencia de clase y variables estáticas para implementarlos (no es válido usar la referencia implícita this, pues estos métodos son independientes de las insatancias creadas); en parte por esto no veo como lograr la versión recursiva de una operación si me atengo a las condiciones antes dichas.
Seguramente este obviando algo...

Gracias y saludos a todos.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Kyshuo Ayame
Moderador Global


Registrado: 07 Ene 2011
Mensajes: 1044

Mensaje Publicado: Martes 13 Nov 2012 18:00

Título del mensaje: Re: Lección 73: Actualizando Nomina Personas - Clases Intern

Responder citando

Amigo Sebastián, perdona la demora en responder, no he podido ni entrar en internet estos días.

Bien, las dudas que te surgen no son triviales. El hecho de implementar la solución con la firma estática justamente es para hacerlo de modo parecido a Modula y por tanto nos olvidamos de cosas como this ya que recibimos el argumento como parámetro de la operación y por tanto, a pesar de que es una variable no estática, podemos acceder a ella porque es un parámetro y por ende existe. Java no nos hará problema, al igual que con el parámetro numérico.

No puedes acceder a variables no estáticas globales a la operación estática, eso está claro, pero no lo necesitas. En una operación estática no existe this porque no es invocada por medio de un objeto ¿Me sigues? por tanto si escsribes this ¿a quién haces referencia? Tienes que hacerlo tal como en Modula, usando el parámetro nodo pasado como argumento y, por ejemplo, hacer dentro un llamado a la misma operación así:

Código:
  1. private static void insertarAlFinal(int n, NodoLista nodo){
  2. if(nodo==null){
  3. . . .
  4. }else{
  5. . . . //Código
  6. insertarAlFinal(n,nodo.siguiente); //Llamado recursivo
  7. }
  8. }


¿Se entiende? Es igual a lo que hacíamos en modula.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
Responder al Tema Ir a página 12Siguiente
Mostrar mensajes anteriores:   
Ir a:  
Todas las horas están en GMT + 2 Horas

Temas relacionados

Tema Autor Foros Respuestas Publicado
El foro no contiene ningún mensaje nuevo

clases y punteros

laephy C, C#, Visual C++ 0 Jueves 28 Feb 2019 12:05 Ver último mensaje
El foro no contiene ningún mensaje nuevo

Dudas al instanciar clases heredadas

MMCA Java 0 Viernes 26 Ene 2018 21:34 Ver último mensaje
El foro no contiene ningún mensaje nuevo

Clases

angeljonh C, C#, Visual C++ 2 Miércoles 30 Ago 2017 06:20 Ver último mensaje
El tema está bloqueado: no pueden editarse ni agregar mensajes.

Busco personas para un proyecto de Facebook

Andres Gonzales Bolsa de trabajo 0 Domingo 19 Mar 2017 22:35 Ver último mensaje
El foro no contiene ningún mensaje nuevo

Uso de clases (no managed beans) en Spring

sapales Java Web 0 Lunes 13 Abr 2015 14:55 Ver último mensaje
Panel de Control
No puede crear mensajes, No puede responder temas, No puede editar sus mensajes, No puede borrar sus mensajes, No puede votar en encuestas,