Ejercicio 2
Plazo de entrega: 3ª semana
Introducción
En este boletín de problemas,
practicará tanto la lectura e interpretación
de las especificaciones, como la lectura y escritura
del código fuente de Java. Implementará
un par de clases que completarán la implementación
de una calculadora gráfica de polinomios, y responderá
a preguntas tanto del código que se le ha facilitado,
como del código que usted ha creado.
Para completar este boletín
de problemas, necesitará saber:
- Álgebra básica
(aritmética de polinomios y aritmética
racional).
- Cómo leer especificaciones
procedurales (requires, modifies, effects)
e invariantes de representación.
- Cómo leer y escribir
código básico en JavaT:
- Estructura de código
y acomodamiento (definición de clase y
método, declaración de campo y variable).
- Llamadas a métodos.
- Estructuras de control:
bucles (while y for) y ramificaciones
condicionales (if, then, else).
- Operadores para:
- La creación de
un objeto: new.
- El acceso a campo y
a método.
- Asignación: =.
- Comparación:
==, !=, <, > , <=, >=.
- Aritmética: +,
-, *, /.
- Cómo ejecutar un compilador
de JavaT como JavaTc para crear ficheros .class.
- Cómo ejecutar la Máquina
Virtual JavaT (MVJ) como Java, para ejecutar tanto
el código como las librerías que el
personal le facilita.
Problema 1: RatNum (15 puntos)
Lea las especificaciones para
RatNum, una clase que representa los números racionales.
Luego, repase la implementación que el personal
ha puesto a su disposición, RatNum.java. Puede
que le sea útil leer detenidamente el código
en RatNum.Test.java para ver ejemplos de cómo se
utiliza la clase RatNum (aunque en vez de verlos en el
código de una aplicación, los verá
en el contexto de un gestor de pruebas). Responda a las
siguientes preguntas y escriba sus respuestas en el fichero
problema1.txt.
¿Qué finalidad
tienen los comentarios de una única línea
al comienzo de los métodos add, sub, mul
y div? Tenga en cuenta que todos estos métodos
requieren que "arg!=null". La razón
es que todos los métodos consiguen acceder a
los campos de 'arg' sin antes comprobar si el valor
de 'arg' es null. No obstante, los métodos también
acceden a los campos de 'this' sin comprobar el valor
'null'; ¿por qué no aparece "this
!=null" en la cláusula requires de
los métodos? RatNum.div(RatNum) comprueba
si su argumento es NaN (no es un número). RatNum.add(RatNum)
y RatNum.mul(RatNum) no hacen eso. Explique: ¿por
qué es RatNum.parse(String) un método
estático? ¿Qué otra alternativa
a los métodos estáticos nos permitiría
lograr el mismo objetivo de generar una RatNum a partir
de una entrada de tipo String? Imagine que el
invariante de representación fuese debilitado
de modo que no necesitásemos que los campos
numer y denom se guardasen en forma reducida.
Esto significa que las implementaciones del método
no podrían asumir ya que ese invariante se mantuviese
en la entrada al método, pero tampoco haría
falta que éstas hicieran cumplir al invariante
a la salida. Luego el nuevo invariante de representación
sería:
// Rep Invariant for every RatNum r: ( r.denom >=
0 )
¿Qué implementaciones
del método o del constructor habría que
cambiar? Para cada parte del código modificado,
describa los cambios de manera informal e indique cuán
más o menos complejo sería el resultado
(teniendo en cuenta tanto la claridad del código
como la eficacia de ejecución). Observe que las
nuevas implementaciones deben ceñirse a la especificación
determinada; en especial, RatNum.unparse() necesita
generar fracciones en forma reducida.
Problema 2: RatPoly (45 puntos)
Vuelva a leer las especificaciones
para las clases RatTerm, RatTermVec y RatPoly. Se le
ha facilitado la implementación de RatTerm y
RatTermVec (no necesita escribir el código).
Asegúrese de que comprende la idea general de
RatPoly y las especificaciones para los determinados
métodos. Lea la escueta implementación
de RatPoly.java que se le ha dado. Las partes más
significativas del fichero que se le ha facilitado son
los comentarios que describen cómo debe usar
los campos que se le dan para implementar esta clase.
Es muy importante que entienda bien el comentario acerca
del invariante de representación, puesto que
los invariantes que defina pueden tener un efecto drástico
en las implementaciones que están permitidas
para los métodos de RatPoly.
Complete la implementación
para los métodos en la especificación
de RatPoly. Puede definir nuevos métodos helper
privados en la medida que desee; nosotros le hemos
sugerido unos cuantos, completos con especificaciones,
pero no está obligado a usarlos. (No obstante,
el personal docente piensa que si los usa, simplificará
enormemente su implementación).
Además, le hemos facilitado
una suite de prueba bastante rigurosa en RatPolyTest.java.
Puede ejecutar esta suite con JUnit a la vez
que puede programar y evaluar su progreso y la exactitud
de su código. Para ejecutar la suite de
prueba sobre RatPoly, escriba el siguiente comando:
athena% java junit.swingui.TestRunner
ex2.RatPolyTest
Para ejecutar JUnit mediante un
método alternativo no gráfico, utilice
junit.textui.TestRunner en vez de junit.swingui.TestRunner,
como aparece indicado en página de consejos.
Indique claramente con comentarios
dentro del código fuente si el código
pasa o no todos los tests. Si no pone ningún
comentario, daremos por hecho que el código falla.
Problema 3: RatPolyStack (25 puntos)
Siga el mismo procedimiento del
problema 2, pero esta vez, rellene los espacios en blanco
para RatPolyStack.java. Puede aplicar las mismas reglas
aquí (puede añadir métodos helper
privados si lo desea). Le hemos facilitado una suite
de prueba en RatPolyStackTest.java. Puede ejecutar
esta suite con JUnit mientras programa y evalúa
su progreso y la exactitud de su código. Para
ejecutar la suite de prueba sobre RatPolyStack,
escriba el siguiente comando:
athena% java junit.swingui.TestRunner
ex2.RatPolyStackTest
Indique claramente con comentarios
dentro del código fuente si el código
pasa o no todos los tests. Si no pone ningún
comentario, daremos por hecho que el código falla.
Problema 4: PolyCalc (5 puntos)
Ahora que ya ha implementado las
dos clases que faltaban en el sistema, puede ejecutar
la aplicación PolyCalc. Dicha aplicación
le permitirá introducir polinomios y realizar
operaciones aritméticas sobre ellos a través
de una interfaz de usuario de tipo point-and-click
(señalar y activar). La calculadora también
realiza una representación gráfica de
los polinomios resultantes. Para ejecutar PolyCalc,
escriba el siguiente comando:
athena% java ex2.PolyCalcFrame
Se abrirá una ventana con
una pila a la izquierda, un visualizador gráfico
a la derecha, un área de texto para introducir
los polinomios y una serie de botones al final. Pulse
los botones para introducir los polinomios y manejar
los que se encuentren en la pila. El visualizador gráfico
se irá actualizando sobre la marcha, mostrando
la representación gráfica de los cuatro
primeros elementos de la pila.
Entregue sus cuatro expresiones
polinómicas favoritas dentro del fichero problema4.txt
en el formato de RatPoly.unparse.
Problema 5: Diagrama de dependencia
de módulos (10 puntos)
Cree un diagrama de dependencia
entre módulos en el que se muestren todas las clases
e interfaces utilizadas en la ejecución del programa
final, con sus relaciones de dependencia y asociación.
No hace falta que
incluya PolyCalcFrame. Debería hacer una valoración
razonable y decidir qué interfaces y librerías
de clases de Java
va a incluir.
Puede que usted (y su monitor de prácticas) encuentren
útil la utilización de Visio para generar
los MDDs (Diagramas de dependencia de módulos.
Consulte la hoja de herramientas para obtener más
información sobre Visio.
Clases que se han facilitado:
Se le han facilitado todas estas clases ya compiladas:
- Con el código fuente
también incluido:
ex2.RatNum
ex2.RatNumTest
ex2.RatPolyTest
ex2.RatPolyStackTest
ex2.Cons (como parte del código de inicio de
RatPolyStack.java)
- Sin el código fuente:
ex2.PublicTest
ex2.PolyGraph
ex2.PolyCalcFrame
ex2.RatTerm
ex2.RatTermVec
Sumérjase
Antes de programar, asegúrese
de que ha seguido las instrucciones sobre el directorio
de instalación y el manejo de Java que
se detallan en la hoja de herramientas:
Cree un directorio con el nombre ex2 y copie en él
los archivos que le hemos facilitado:
mkdir ~/6.170/ex2
cp -p /mit/6.170/www/psets/ex2/ex2-spec/* ~/6.170/ex2
Luego, edite los ficheros de especificación
dentro de su implementación y pruébela.
No hace falta que compile los otros ficheros de código
fuente que le hemos dado; los archivos de clase ya compilados
están listos para ser utilizados en el directorio
del curso 6.170. Observe las Especificaciones para las
clases que implementará, y aquellas que le hemos
facilitado.
Al final del boletín de
problemas, en su directorio ex2, debería tener
los siguientes archivos listos para entregar: problem1.txt
, RatPoly.java, RatPolyStack.java y problem4.txt, junto
con el archivo Manifest, en el que se enumeren todas
las entradas.
Consejos
- Observe las directrices y los
consejos del boletín de problemas.
- ¡Piense antes de empezar
a escribir el código! Las funciones aritméticas
de los polinomios no son difíciles, pero si
empieza a desarrollar la implementación sin
un plan previo específico, es fácil
que se vea envuelto en una terrible enramada.
- JUnit vuelve a cargar todas
las clases cada vez que se ejecuta un test,
así que no es necesario reiniciar la aplicación
JUnit después de hacer cambios en el código
de Java (aunque sí hace falta que vuelva a
compilar su código para que los cambios se
apliquen).
- Para ejecutar JUnit mediante
un método alternativo no gráfico, utilice
junit.textui.TestRunner en vez de junit.swingui.TestRunner.
- Las suites de prueba
que se le han facilitado en el boletín de problemas
1 son las mismas que usaremos para evaluar su implementación;
en los posteriores boletines de problemas, no se le
facilitará una serie de casos de prueba tan
rigurosa para que ejecute sus implementaciones, pero
para esta serie de problemas, puede dar por supuesto
que el conjunto de tests que se le ha facilitado
es lo suficientemente riguroso como para no tener
que escribir uno propio.
- La división de polinomios
entre números racionales es igual que la gran
división que uno aprende en la escuela primaria.
Le damos un ejemplo aquí:
Errata
Había un pequeño
error en la implementación del código que
se facilitó, en RatPoly.java. Esto hizo que los
estudiantes encontraran errores NaN al ejecutar los casos
de prueba. Para solucionarlo, haga lo siguiente:
Inserte las siguientes líneas de código
en RatPoly.appendTerm(StringBuffer sb, RatTerm rt):
if(sb.toString().equals("NaN"))
{
return;
}
if(c.isNaN())
{
sb.replace(0, sb.length(), "NaN");
return;
}
justo después de las declaraciones:
RatNum c = rt.coeff;
int e = rt.expt;
al comienzo del método.
En el Problema 2 se expuso de manera
incorrecta que "la suite de prueba depende
enormemente de la implementación del método
de RatPoly.unparse(); si la suite de prueba afirma
que todos o muchos de los tests están fallando,
el motivo del problema podría ser que hay un
error en su implementación de unparse()."
El método unparse() se le facilita implementado.
Preguntas
& Respuestas
En esta sección se enumerarán
una serie de aclaraciones y respuestas a las típicas
preguntas que suelen surgir en relación a los boletines
de problemas. Intentaremos mantenerla lo más actualizada
posible, así que este debería ser el primer
lugar que usted consultara (después de haber releído
atentamente las especificaciones y el boletín de
problemas entregado en clase) cuando le surgiera cualquier
tipo de problema.
Pregunta: ¿Qué
debería devolver RatPoly.eval() en una clase
RatPoly en la que isNaN() es igual a "true"?
Respuesta: La clase java.lang.Double
tiene una variable estática final de tipo double
llamada Double.NaN que representa un NaN. Esto
es lo que le debería devolver en este caso.
Pregunta: ¿Debería
RatPoly(0,n) dar/crear
(make) un polinomio de grado "0"? ¿Como
se supone que debo implementar polinomios de grado "0"?
¿Puedo representarlo como un término con
coeficiente cero?
Respuesta: Si, RatPoly(0,n)
debería dar
un polinomio de grado "0". Sin embargo, no
puede crear un término con coeficiente cero para
representarlo, porque violaría el invariante
de representación de RatPoly. Debería
leer atentamente el último enunciado de la función
de abstracción de RatPoly: " Si no hay términos,
RatPoly representará el polinomio de grado cero".
Pregunta: Pero no lo entiendo...
¿No dice el invariante de representación
que los términos no pueden tener valor "null"?
¿Cómo es que entonces puede no haber términos?
Respuesta: Claro, pero puede
estar vacío. Dése cuenta de que mientras
el invariante de representación declara que el
campo de los términos no puede ser "null",
puede que el campo de los términos haga referencia
a un objeto RatTermVec que no contiene ningún
término. (Esto haría que terms.size()
devolviera el valor 0). Por favor, póngase en
contacto con su monitor de prácticas si aún
sigue con dudas sobre esto. (Por favor, échele
un vistazo a los comentarios del código de RatPoly
para la función de abstracción y el invariante
de representación).
Pregunta: ¿Por qué
el argumento coeff es un int, en lugar de un RatNum,
para el constructor de Ratpoly?
Respuesta: Nuestros casos
de prueba utilizan este constructor. No proporcionamos
otro constructor que tome un RatNum (numero racional)
como coeficiente porque RatPoly.parse() es un método
mucho más útil para crear nuevos RatPolys
(polinomios racionales).
Pregunta: Pero no lo entiendo...
¿No dice el invariante de representación
que los términos no pueden tener valor "null"?
¿Cómo es que entonces puede no haber términos?
Respuesta: Claro, pero puede
estar vacío. Dése cuenta de que mientras
el invariante de representación declara que el
campo de los términos no puede ser "null",
puede que el campo de los términos haga referencia
al objeto RatTermVec que no contiene ningún término.
(Esto haría que terms.size() devolviera el valor
0). Por favor, póngase en contacto con su monitor
de prácticas si aún sigue con dudas sobre
esto. (Por favor, échele un vistazo a los comentarios
del código de RatPoly para la función
de abstracción y el invariante de representación).
Pregunta: Tuve un problema
con Visio 2000; al arrancarlo se abría un cuadro
de diálogo de error que decía: "The
procedure entry point GBL could not be located in the
dynamic link library VISLIB32.DLL?" ("¿No
se ha podido localizar el punto de entrada al método
GBL en la librería de enlaces dinámicos
VISLIB32.DLL?"). Estoy instalando Visio 2000 en
un ordenador con plataforma Windows 2000.
Respuesta: Consulte la Ayuda
de Microsoft: Visio2000: Mensaje de error: The Procedure
Entry Point GBL Could Not Be Located in the Dynamic
Link Library Vislib32.dll. (El punto de entrada
al método GBL no podría ubicarse en la
librería de enlaces dinámicos Vislib32.dll).
Pregunta: ¿Podría
hacer el Ejercicio 2 en el ordenador de mi casa (no
es un Athena)?
Respuesta: Claro, pero deberá
descargarse en su ordenador de casa los correspondientes
archivos jar que se encuentran en el directorio del
curso 6.170. Para el Ejercicio 2, necesitará
los archivos junit.jar y ex2-lib.jar que están
en el directorio /mit/6.170/lib. Sin embargo, su código
DEBE funcionar en Athena, así que por favor,
dedíquele unos cuantos minutos para asegurarse
de que funciona correctamente en Athena antes de entregar
los Ejercicios. [Descargar ex2-lib.jar] [Descargar junit.jar]
Pregunta: ¿Dónde
puedo encontrar la API de JUnit?
Respuesta: Puede encontrar
aquí la API de JUnit.
|