5 Tratamientos secuenciales - v4 · 2! IntroducciónalaProgramación!! •...
Transcript of 5 Tratamientos secuenciales - v4 · 2! IntroducciónalaProgramación!! •...
Introducción a la Programación Tema 5. Tratamientos Secuenciales 1. Introducción ......................................................................................................................... 1
2. Esquemas Iterativos ............................................................................................................. 5
2.1 Suma ............................................................................................................................. 5
2.2 Producto ....................................................................................................................... 7
2.3 Contador ....................................................................................................................... 8
2.4 Selecciona Lista ............................................................................................................. 8
2.5 Selecciona Conjunto ..................................................................................................... 9
2.6 Selecciona Array ........................................................................................................... 9
2.7 Ejecuta Para Todo ....................................................................................................... 10
2.8 Para Todo .................................................................................................................... 10
2.9 Existe ........................................................................................................................... 11
2.10 Busca Primero ............................................................................................................. 12
2.11 Posición del Primero ................................................................................................... 13
2.12 Máximo: ...................................................................................................................... 14
2.13 Mínimo: ...................................................................................................................... 17
2.14 Iteración Genérica ...................................................................................................... 19
2.15 Algoritmos para grupos consecutivos de elementos: ................................................ 22
3. Ejemplos y Metodología de trabajo ................................................................................... 23
4. Conceptos aprendidos ........................................................................................................ 29
5. Ejercicios Propuestos ......................................................................................................... 29
1. Introducción
Al tratar con agregados de datos (listas, conjuntos, arrays, cadenas de caracteres, secuencias de valores y en general todos los tipos que implementen la interfaz Iterable) es muy común buscar información sobre cantidades agregadas: máximos o mínimos, posiciones de elementos en el agregado, etc. Estos tratamientos se repiten continuamente. Nuestro objetivo en este tema es estudiar estos tratamientos sobre agregados de datos, hacer un catálogo lo más extenso posible de los mismos y, a ser posible, implementarlos en métodos que puedan ser reutilizados.
Dado un agregado de objetos podemos plantearnos preguntas del tipo:
2 Introducción a la Programación
• ¿cuánto vale la suma de una expresión de los objetos del agregado? • ¿y el producto de los que verifican una propiedad? • ¿existe algún objeto que verifique una determinada condición? • ¿cuáles son los objetos que cumplen una determinada propiedad? • ¿cuál es el valor más grande de los que cumplen una condición? • ¿según un orden dado cuál es mayor elemento para una condición? • actualiza todos los objetos que cumplen una condición • almacena en una lista los objetos que cumplan una condición • etc.
Cada una de esas preguntas y otras similares pueden ser respondidas con métodos que tienen una estructura común. Ejemplo: ¿Cómo se calcula la suma de los cuadrados de los números enteros contenidos en una lista de Enteros? Como el agregado es una lista usamos un for extendido para recorrerla. El código lo concretamos en un método que tomando una lista como parámetro y devuelve el resultado esperado.
public static Integer sumaCuadrados(List<Integer> v){ Integer suma=0; for (Integer e: v){
suma = suma + e*e; } return suma; }
Igualmente podemos preguntarnos por la suma de los cuadrados de todos los enteros que van desde un entero dado, a, hasta otro b (mayor que a) en pasos de c (positivos). Por ejemplo la secuencia 10, 12, 14, 16, 18, 20 sería especificada por a=10, b=20, c=2. Para generar la secuencia usamos un for clásico. El método resultante es:
public static Integer sumaCuadradosIntervalo(Integer a, Integer b, Integer c){ if(!(b>a && c>0)) throw new IllegalArgumentException(); Integer suma=0; for (Integer e=a; e<=b; e=e+c){
suma = suma + e*e; } return suma; }
El método anterior dispara una excepción si los parámetros recibidos no cumplen lo especificado. Salvando la comprobación de la idoneidad de los parámetros ambos métodos tienen, como vemos, la misma estructura.
3 5. Tratamientos secuenciales
Veamos ahora un problema más general: sumar los cuadrados de los enteros contenidos en una lista que son múltiplos de 3.
public static Integer sumaCuadradosMultiplos3(List<Integer> v){ Integer suma=0; for(Integer e: v){ if(Enteros.esMultiplo(e,3)){
suma = suma + e*e; } } return suma; }
O en la secuencia definida por a, b, c. Es decir los enteros desde a hasta b con incrementos de c.
public static Integer sumaCuadradosIntervaloMultiplos3(Integer a, Integer b, Integer c){ if(!(b>a && c>0)) throw new IllegalArgumentException(); Integer suma=0; for(Integer e= a; e<=b; e=e+c){ if(Enteros.esMultiplo(e,3)){
suma = suma + e*e; } } return suma; }
En estos métodos aparece una estructura que se repetirá. Esta estructura tiene varios elementos a destacar: acumulador, filtro, expresión.
• Acumulador: es una variable que acumula el resultado calculado hasta ese momento. Se inicializa al valor adecuado para la secuencia o el agregado vacío y es del tipo del resultado que queramos calcular. En este caso la variable es suma que se inicializa a cero y es de tipo Integer como el resultado.
• Filtro: es una expresión lógica sin efectos laterales que nos sirve para escoger los elementos del agregado sobre los que vamos a hacer el cálculo. En este caso es la expresión Enteros.esMultiplo(e,3).
• Expresión: es una expresión que a partir de cada objeto del agregado calcula el valor que se va acumulando. En este caso la expresión es e*e.
• Fuente de Datos: Es el agregado a partir del cual queremos calcular el valor acumulado. En los tratamientos secuenciales distinguiremos tres tipos de fuentes de datos:
4 Introducción a la Programación
o Agregados que ofrecen el interface Iterable<T>. Son los más generales y, como veremos en los próximos capítulos, los demás tipos de agregados pueden ser convertidos a éste. Para recorrerlos usamos un for extendido.
o Arrays. Es decir agregados de tipo T[]. Para recorrerlos podemos usar un for extendido o un for clásico.
o Secuencias de números. Pueden ser generadas mediante un for clásico. Veremos ejemplos en adelante.
Junto a los conceptos anteriores podemos introducir brevemente otros dos importantes: el invariante y la función de cota:
• Invariante: es una expresión lógica que se mantiene verdadera en cada una de las iteraciones. En nuestro caso el invariante es: la variable acumulador contiene el resultado de acumular los valores de la Expresión calculados sobre aquellos elementos recorridos hasta ese momento que cumpliendo la condición de Filtro.
• Función de Cota: el número de elementos del agregado que faltan por recorrer. Los conceptos anteriores son importantes para un buen diseño de los métodos. La Función de Cota de disminuir en cada iteración y tener un valor mayor o igual a cero. Si cumple etas propiedades tenemos garantía que el método termina. El invariante nos indica una propiedad que se conserva. En el ejemplo anterior la variable suma es, en cada momento, igual a la suma de los elementos recorridos del iterable que cumplen el filtro. Esta propiedad es verdadera al terminar la iteración. Como el Invariante se va manteniendo verdadero y la Función de Cota es adecuada podemos concluir que los métodos están bien diseñados. En los diseños anteriores hemos asumido que el origen de datos no contiene el valor null. Esto es cierto en la secuencia de enteros pero puede no serlo cuando el origen de datos es una lista o un array. Cuando el origen de datos puede contener null, o simplemente no tenemos garantías de que no aparecerá un valor null, entonces hay que reforzar la expresión que hace de filtro para que el esquema funcione correctamente e impedir que se disparen excepciones.
public static Integer sumaCuadradosMultiplos3(List<Integer> v){ Integer suma=0; for(Integer e: v){ if(e!=null && Enteros.esMultiplo(e,3)){
suma = suma + e*e; } } return suma; }
Por robustez es muy importante realizar esta comprobación.
5 5. Tratamientos secuenciales
2. Esquemas Iterativos Dependiendo de lo que queramos acumular. A estas estructuras de uso muy frecuente las denominaremos Esquemas Iterativos. Todos ellos comparten los elementos explicados anteriormente: fuente de datos, acumulador, filtro y expresión. Para dar los detalles de cada uno de ellos tenemos que indicar:
• Su objetivo. Por ejemplo sumar los valores del agregado si estos son numéricos. • Tipo del acumulador: en el caso de la suma es Double, Integer u otro tipo numérico. • Valor Inicial: valor del acumulador si el agregado está vacío. En el caso de la suma es
cero. • Expresión acumuladora: Es la forma concreta en la que se actualiza el valor del
acumulador en cada iteración. De estas expresiones acumuladoras tenemos dos tipos: o El acumulador, un operador binario y una expresión. En el caso de la suma es
el operador binario +. o Cuando el acumulador es de tipo objeto la expresión acumuladora se
compone de la llamada a un método del acumulador pasándole como parámetro la expresión a acumular.
Los esquemas iterativos podemos diseñarlos todos como una misma estructura general pero en algunos casos es conveniente tener esquemas alternativos. Esto ocurre cuando se puede obtener el resultado sin necesidad de recorrer todos los elementos del agregado o cuando el esquema puede simplificarse haciendo un tratamiento específico del primer elemento. Veremos en primer lugar los métodos similares a la suma. Todos ellos tienen la misma estructura que la vista para la suma. Para cada método mostraremos su objetivo, su código y las características anteriores. 2.1 Suma
• Objetivo: Suma de los valores calculados por la expresión sobre los elementos que cumplen el filtro.
• Tipo del acumulador: Double, Integer u otro tipo numérico. • Valor Inicial: Cero. • Expresión acumuladora: El acumulador más la expresión. • Operador binario: + • Invariante: la variable suma contiene el resultado de sumar los valores de la Expresión
calculados sobre aquellos elementos recorridos hasta ese momento que cumplen la condición de Filtro.
• Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código:
Iterable<T> agregado = ...; Double suma = 0.0;
for (T e : agregado) {
if (Filtro) {
6 Introducción a la Programación
suma = suma + Expresion; } }
En el código anterior hemos asumido que el valor que valor a sumar es de tipo Double y que la fuente de datos es de tipo Iterable<T>. Filtro es una expresión sobre la variable e que devuelve boolean. Esta expresión que hace el pael de Filtro debe tener en cuenta en su caso la posibilidad de que algún valor sea null como se ha explicado más arriba. Expresion es una expresión sobre la variable e que devuelve Double. El esquema es similar si el tipo del valor a acumular es cualquier tipo numérico. Por ejemplo si el valor a acumular es Integer este será el tipo de la variable suma, que se inicializará a 1, y Expresion devolverá un valor de tipo Integer.
Si el filtro fuera siempre true, es decir no hay filtro el esquema se simplifica a:
Iterable<T> agregado = ...; Double suma = 0.0;
for (T e : agregado) { suma = suma + Expresion; }
Si la fuente de datos fuera un array entonces el esquema se puede realizar de dos maneras: usando un for extendido como en el caso anterior o usando un for clásico. Las dos formas son:
T[] agregado = ...; Double suma = 0.0;
for (T e : agregado) { if (Filtro) { suma = suma + Expresion; } }
T[] agregado = ...; Double suma = 0.0;
for (int i = 0; i < agregado.length; i++) { T e = agregado[i]; if (Filtro) { suma = suma + Expresion; } }
Donde podemos hacer los mismos comentarios sobre el tipo del valor a acumular. Si la fuente de datos fuera una secuencia de números reales, por ejemplo una secuencia aritmética desde a1 hasta a2 con razón a3, entonces el esquema usaría un for clásico tal como se muestra abajo:
Double suma = 0.0; for (Double e = a1; e <= a2; e = e+a3) {
if (Filtro) {
7 5. Tratamientos secuenciales
suma = suma + Expresion; } }
El agregado de partida puede tener una estructura más compleja. Veamos simplemente un ejemplo el caso de un agregado de tipo Iterable<T> y donde cada objeto de tipo T tiene una propiedad P1 de tipo Iterable<T2>. Supongamos que queremos calcular la suma de los valores devueltos por una expresión sobre los elementos de tipo T2 que pasen un filtro. Ahora hay que utilizar dos for extendidos anidados. El primero recorre el iterable exterior y el segundo recorre cada uno de los elementos que forman parte de la propiedad P1.
Iterable<T> agregado = ...; Double suma = 0.0;
for (T e : agregado) { for(T2 e1 : e.getP1()){ if (Filtro) { suma = suma + Expresion; } } }
Ahora Filtro es una expresión construida sobre la variable e1 y que devuelve Boolean teniendo en cuenta los posibles valores null. La Expresión se construye sobre la variable e1 y devuelve Double. Este esquema podría ser extendido a niveles mayores de anidamiento. En todos los casos el resultado quedaría en la variable suma.
En los esquemas siguientes sólo mostramos la versión cuando la fuente de datos es de tipo Iterable<T> asumiendo que los esquemas para fuente de datos de los otros tipos citados pueden ser obtenidos fácilmente.
2.2 Producto
Objetivo: Producto de los valores calculados por la expresión sobre los elementos que cumplen el filtro.
• Objetivo: Multiplicar los valores calculados por la expresión sobre los elementos que cumplen el filtro.
• Tipo del acumulador: Double, Integer u otro tipo numérico. • Valor Inicial: Uno • Expresión acumuladora: El acumulador por la expresión. • Operador binario: * • Invariante: la variable producto contiene el resultado demultiplicar los valores de la
Expresión calculados sobre aquellos elementos recorridos hasta ese momento que cumplen la condición de Filtro.
• Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código:
8 Introducción a la Programación
Iterable<T> agregado = ...; Double producto = 1.0;
for (T e : agregado) { if (Filtro) { producto = producto * Expresion; } }
El resultado quedaría en la variable producto.
2.3 Contador
• Objetivo: Cuenta el número de elementos que cumplen el filtro • Tipo del acumulador: Integer • Valor Inicial: 0 • Expresión acumuladora: Sumar uno al acumulador • Invariante: la variable num contiene el resultado de contar el número de elementos
recorridos hasta ese momento que cumplen la condición de Filtro. • Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código:
Iterable<T> agregado = ...; Integer num = 0;
for (T e : agregado) { if (Filtro) { num = num + 1; } }
El resultado quedaría en la variable num.
2.4 Selecciona Lista
• Objetivo: Devuelve en una Lista con los valores calculados por una expresión sobre los objetos que pasan el filtro.
• Tipo del acumulador: List<T2> • Valor Inicial: Lista Vacía. • Operador binario: add • Invariante: la variable acumulador contiene una lista con los valores calculados por la
expresión sobre los elementos recorridos que cumplen la condición de Filtro. • Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código:
Iterable<T> agregado = ...; List<T2> lrr = new ArrayList();
for (T e : agregado) {
9 5. Tratamientos secuenciales
if (Filtro) { lrr.add(Expresion); } }
El resultado quedaría en la variable lr. La Expresión tiene que devolver un valor del tipo T2 a partir de un elemento de tipo T.
2.5 Selecciona Conjunto • Objetivo: Devuelve un Conjunto con los valores calculados por una expresión sobre los
objetos que pasan el filtro. • Tipo del acumulador: Set<T> • Valor Inicial: Conjunto Vacío. • Operador binario: add • Invariante: la variable acumulador contiene un conjunto con los valores calculados por
la expresión sobre los elementos recorridos que cumplen la condición de Filtro. • Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código:
Iterable<T> agregado = ...; Set<T2> sr = new HashSet();
for (T e : agregado) { if (Filtro) { sor.add(Expresion); } }
El resultado quedaría en la variable sr. La Expresión tiene que devolver un valor del tipo T2 a partir de un elemento de tipo T.
2.6 Selecciona Array • Objetivo: Devuelve un array con los valores calculados por una expresión sobre los
objetos que pasan el filtro. • Invariante: la variable ar contiene los valores calculados por la expresión sobre los
elementos recorridos que cumplen la condición de Filtro en las posiciones que van de 0 a i-‐1
• Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código:
Iterable<T> agregado = ...; T2[] ar ; Integer num = 0;
for (T e : agregado) { if (Filtro) { num++; }
}
10 Introducción a la Programación
int i = 0; ar = new T2[num];
for (T e : agregado) { if (Filtro) { ar[i] = Expresion; i++; } }
El resultado quedaría en la variable ar. La Expresión tiene que devolver un valor del tipo T2 a partir de un elemento de tipo T.
El esquema es más complejo que en el caso de seleccionar listas o conjuntos. La razón es que los array son agregados de datos de tamaño constante. Por lo tanto debemos conocer su tamaño para poder crearlos. Para poder hacerlo debemos contar los elementos que pasar el filtro, luego crear un array de ese tamaño y por último colocar los sucesivos valores calculados en las casillas que tienen un índice i dado. El índice se irá actualizando a media que vamos encontrando objetos que pasan el filtro.
2.7 Ejecuta Para Todo
• Objetivo: Ejecuta una acción sobre cada objeto que pase el filtro. • Tipo del acumulador: No hay • Valor Inicial: No hay • Operador binario: No hay • Invariante: Se ha ejecutado la Accion sobre los elementos recorridos que cumplen la
condición de Filtro. • Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código:
Iterable<T> agregado = ...;
for (T e : agregado) { if (Filtro) { Accion; } }
Una acción es una sentencia construida sobre la variable e que puede producir efectos laterales sobre la misma. Es decir cambiar su estado. Veamos ahora el segundo bloque: operaciones que pueden calcular su valor en muchos casos sin recorrer completamente el agregado. Los esquemas tienen una estructura similar pero la iteración puede terminar, con la sentencia break, cuando ya se conozca el resultado. El código debe tratar el primer elemento de una forma especial. 2.8 Para Todo
• Objetivo: Decide si todos los elementos filtrados cumplen una propiedad representada por Expresion.
11 5. Tratamientos secuenciales
• Tipo del acumulador: Boolean • Valor Inicial: true • Operador binario: && • Invariante: la variable r es verdadera si los valores calculados por la expresión sobre los
elementos recorridos que cumplen la condición de Filtro son todos verdaderos en otro caso tiene el valor falso.
• Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código:
Iterable<T> agregado = ...; Boolean r = true;
for (T e : agregado) { if (Filtro) { r = r && Expresion; if(!r) break; } }
Ahora Filtro y Expresion son dos expresiones construidas sobre la variable e que devuelven Boolean. Dadas las propiedades del operador && una vez que la variable r tome el valor false, es decir la Expresion devuelve false sobre un elemento que pasa el Filtro, permanecerá a false hasta el final de la iteración. Por ello es posible salirse del bucle cuando r sea false. El resultado queda en la variable r. Dado que la variable r cambia de valor, de true a false, cuando Expresion devuelve false y en ese momento nos salimos del bucle el valor de r es simpre el mismo que el de la Expresion. Es decir true hasta que aparece un valor false para Expresion. Obtenemos un esquema equivalente si sustituimos r = r && Expresion por r = Expresion. Por lo tanto el esquema anterior es equivalente a:
Iterable<T> agregado = ...; Boolean r = true;
for (T e : agregado) { if (Filtro) { if(!Expresion){ r = false; break; } } }
2.9 Existe
• Objetivo: Decide si alguno de los elementos filtrados cumplen una propiedad representada por Expresion
• Tipo del acumulador: Boolean • Valor Inicial: false • Operador binario: ||
12 Introducción a la Programación
• Invariante: la variable r es verdadera si alguno de valores calculados por la Expresión sobre los elementos recorridos que cumplen la condición de Filtro es verdadero en otro caso tiene el valor falso.
• Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código:
Iterable<T> agregado = ...;
Boolean r = false; for (T o : agregado) { if (Filtro) { r = r || Expresion; if(r) break; } }
Ahora Filtro y Expresion son dos expresiones construidas sobre la variable e que devuelven Boolean. Dadas las propiedades del operador || una vez que la variable r tome el valor true, es decir la Expresion devuelve true sobre un elemento que pasa el Filtro, permanecerá a true hasta el final de la iteración. Por ello es posible salirse del bucle cuando r sea true. El resultado queda en la variable r. Dado que la variable r cambia de valor, de f a false, cuando Expresion devuelve false y en ese momento nos salimos del bucle el valor de r es simpre el mismo que el de la Expresion. Es decir true hasta que aparece un valor false para Expresion. Obtenemos un esquema equivalente si sustituimos r = r && Expresion por r = Expresion. Por lo tanto el esquema anterior es equivalente a: Dado que la variable r cambia de valor, de false a true, cuando Expresion devuelve true y en ese momento nos salimos del bucle tenemos que el valor de r será el mismo si r = r || Expresion por r = Expresion. Por lo tanto el esquema anterior es equivalente a:
Iterable<T> agregado = ...; Boolean r = false;
for (T e : agregado) { if (Filtro) { if(Expresion){ r = true; break; } } }
2.10 Busca Primero
• Objetivo: buscar, si existe, el primer objeto, de entre los filtrados, que cumple una propiedad representada por Expresion. Si no existe dispara la excepción NoSuchElementException
• Tipo del acumulador: Boolean
13 5. Tratamientos secuenciales
• Valor Inicial: false • Operador binario: || • Invariante: la variable r es verdadera si alguno de valores calculados por la Expresión
sobre los elementos recorridos que cumplen la condición de Filtro es verdadero en otro caso tiene el valor falso.
• Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código: Es el caso concreto del esquema existe pero donde devolvemos el elemento
encontrado en vez de un valor lógico. Las dos posibilidades son: Iterable<T> agregado = ...;
Boolean r = false; T elemento = null; for (T o : agregado) { if (Filtro) { r = r || Expresion; if(r) { elemento = null; break; } } } if(elemento == null) throw new NoSuchElementException();
O equivalentemente, siguiendo la segunda versión del esquema Existe:
Iterable<T> agregado = ...; T elemento = null;
for (T e : agregado) { if (Filtro) { if(Expresion){ elemento = e; break; } } } if(elemento == null) throw new NoSuchElementException();
Como vemos en este esquema el resultado queda en la variable elemento. La variable r se hace innecesaria en la segunda versión del esquema.
2.11 Posición del Primero • Objetivo: buscar, si existe, la posición del primer objeto, de entre los filtrados, que
cumple una propiedad representada por Expresion. Si no existe devuelve -‐1. Tipo del acumulador: Boolean
• Valor Inicial: false • Operador binario: ||
14 Introducción a la Programación
• Invariante: la variable r es verdadera si alguno de valores calculados por la Expresión sobre los elementos recorridos que cumplen la condición de Filtro es verdadero en otro caso tiene el valor falso.
• Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código: Es el caso concreto del esquema existe pero donde devolvemos un entero con
la posición en vez de un valor lógico. Necesitamos, además, una variable que mantenga el valor de la posición. . Las dos posibilidades son: Iterable<T> agregado = ...;
Boolean r = false; int r = -1; int pos = 0; for (T o : agregado) { if (Filtro) { r = r || Expresion; if(r) { r = pos; break; } } pos++; }
O equivalentemente, siguiendo la segunda versión del esquema Existe:
Iterable<T> agregado = ...; int r = -1; int pos = 0; for (T e : agregado) { if (Filtro) { if(Expresion){ r = pos; break; } } pos++; }
Como vemos en este esquema el resultado queda en la variable r que ahora es de tipo entero.
Veamos ahora algunos valores acumulados que dependen de órdenes. 2.12 Máximo:
• Objetivo: Calcula el máximo de los valores calculados por una expresión sobre los elementos que cumplen el filtro. El máximo se calcula con respecto a un orden dado. Si el agregado es vacío o no hay elementos que pasen el filtro devuelve una excepción.
• Tipo del acumulador: T2.
15 5. Tratamientos secuenciales
• Valor Inicial: null considerado, como veremos, como un valor menor que cualquier otro del tipo
• Expresión acumuladora: El máximo según un orden del acumulador y la Expresión. • Operador binario: max dado un orden. • Invariante: la variable elMax contiene el valor máximo de los valores de la Expresión
calculados sobre aquellos elementos recorridos hasta ese momento que cumplen la condición de Filtro. Si no hubiera ninguno el valor sería null.
• Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código
Iterable<T> agregado = ...; Ordering<T2> ord = ...; Ordering<T2> ord1 = ord.nullsFirst(); T2 elMax = null;
for (T e : agregado) { if (Filtro) { elMax = ord1.max(elMax,Expresion); } } if(elMax == null) throw new NoSuchElementException();
El resultado queda en la variable elMax. La Expresión tiene que devolver un valor del tipo T2 a partir de un elemento de tipo T. El diseño se basa el escoger un valor, que es el menor que todos los valores del tipo en cuestión, para inicializar elMax. Escogemos null como es valor para todos los tipos.
Usamos las posibilidades de la clase Ordering vista en el tema anterior para simplificar el código. En la línea segunda inicializamos un objeto de tipo Ordering a partir del orden natural de un tipo, a partir de un Comparator u otras posibilidades que se ofrecen por la clase. Para conseguir que según un orden dado el valor null sea menor que todos los valores del tipo obtenemos un nuevo objeto de tipo Ordering mediante el método nullFirst(). Como vemos el diseño es similar al caso del esquema suma. Otra forma posible es reforzar el filtro para eliminar todos los objetos que son menores que el mayor encontrado hasta ahora y actualizar del el objeto máximo encontrado.
Iterable<T> agregado = ...; Ordering<T2> ord = ...; Ordering<T2> ord1 = ord.nullsFirst(); T2 elMax = null;
for (T e : agregado) { if (Filtro && ord1.compare(Expresion,elMax) > 0) { elMax = Expresion; } } if(elMax == null) throw new NoSuchElementException();
El diseño anterior para el máximo se ha basado en escoger un primer valor para elMax es menor que cualquier valor para el tipo. Otra posibilidad es escoger para elMax el primer valor
16 Introducción a la Programación
del agregado. Esto se consigue haciendo un tratamiento específico del primer elemento. Esta técnica puede ser usada en otros contextos. Consiste en definir una variable de tipo boolean inicializada a true que cambia su valor cuando hacemos el tratamiento específico del primer elemento. El esquema de cálculo del máximo es de la forma:
Iterable<T> agregado = ...; Ordering<T2> ord = ...; T2 elMax = null; Boolean esElPrimero = true;
for (T e : agregado) { if (Filtro) { if(esElPrimero){ elMax = Expresion; esElPrimero = false; } else { elMax = ord.max(elMax,Expresion); } } } if(elMax == null) throw new NoSuchElementException();
Como vemos ahora el orden no debe preocuparse por los valores nulos. Esta misma idea puede usarse en la variante del esquema que refuerza el filtro en la forma:
Iterable<T> agregado = ...; Ordering<T2> ord = ...; T2 elMax = null; Boolean esElPrimero = true;
for (T e : agregado) { if (Filtro) { if(esElPrimero){ elMax = Expresion; esElPrimero = false; } else if (ord.compare(Expresion,elMax) > 0 ) { elMax = Expresion; } } } if(elMax == null) throw new NoSuchElementException();
Cuando el tipo T2 se instancia a algunos tipos particulares (como Integer, Double, etc.) conocemos un valor del tipo que es menor que todos los demás y la expresión para decidir si un valor del tipo es mayor que otro es sencilla el esquema puede concretarse más. Un ejemplo con T2 instanciado a Integer:
Iterable<T> agregado = ...; Integer elMax = Integer.MIN_VALUE;
for (T e : agregado) { if (Filtro && Expresion > elMax) { elMax = Expresion;
} } if(elMax == Integer.MIN_VALUE) throw new NoSuchElementException();
17 5. Tratamientos secuenciales
O usando la técnica de tratar específicamente el primer elemento y sin asumir un valor que sea menor que todos los del tipo:
Iterable<T> agregado = ...; Ordering<T2> ord = ...; Integer elMax = null; Boolean esElPrimero = true;
for (T e : agregado) { if (Filtro) { if(esElPrimero){ elMax = Expresion; esElPrimero = false; } else if (Expresion > elMax) { elMax = Expresion; } } } if(elMax == null) throw new NoSuchElementException();
Ahora inicializar elMax a null solamente nos sirve para decidir al final si ha habido algún elemento que haya pasado el filtro. 2.13 Mínimo:
• Objetivo: Calcula el mínimo de los valores calculados por la expresión sobre los elementos que cumplen el filtro. El mínimo se calcula con respecto a un orden dado. Si el agregado es vacío o no hay elementos que pasen el filtro devuelve una excepción.
• Tipo del acumulador: T2. • Valor Inicial: null considerado, como veremos, como un valor mayor que cualquier otro
del tipo • Expresión acumuladora: El mínimo según un orden del acumulador y la Expresión. • Operador binario: min dado un orden. • Invariante: la variable elMin contiene el valor mínimo de los valores de la Expresión
calculados sobre aquellos elementos recorridos hasta ese momento que cumplen la condición de Filtro. Si no hubiera ninguno el valor sería null.
• Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código
Iterable<T> agregado = ...; Ordering<T2> ord = ...; Ordering<T2> ord1 = ord.nullsLast(); T2 elMin = null;
for (T e : agregado) { if (Filtro) { elMin = ord1.min(elMin,Expresion); } }
if(elMin == null) throw new NoSuchElementException();
18 Introducción a la Programación
El resultado queda en la variable elMin. La Expresión tiene que devolver un valor del tipo T2 a partir de un elemento de tipo T.
Como anteriormente usamos las posibilidades de la clase Ordering vista en el tema anterior para simplificar el código. En la segunda línea inicializamos un objeto de tipo Ordering a partir del orden natural de un tipo, a partir de un Comparator u otras posibilidades que se ofrecen por la clase. Para conseguir que según un orden dado el valor null sea mayor que todos los valores del tipo obtenemos un nuevo objeto de tipo Ordering mediante el método nullLast(). Como vemos el diseño es similar al caso del esquema suma. . Otra forma posible es reforzar el filtro para eliminar todos los objetos que son mayores que el mayor encontrado hasta ahora y actualizar del el objeto máximo encontrado.
Iterable<T> agregado = ...; Ordering<T2> ord = ...; Ordering<T2> ord1 = ord.nullsLast(); T2 elMin = null;
for (T e : agregado) { if (Filtro && ord1.compare(Expresion,elMin) < 0) { elMin = Expresion; } } if(elMin == null) throw new NoSuchElementException();
Como en el caso del máximo hay otra alternativa haciendo un tratamiento específico del primer elemento. El esquema de cálculo del mínimo es de la forma:
Iterable<T> agregado = ...; Ordering<T2> ord = ...; T2 elMin = null; Boolean esElPrimero = true;
for (T e : agregado) { if (Filtro) { if(esElPrimero){ elMax = Expresion; esElPrimero = false; } else { elMax = ord.min(elMax,Expresion); } } } if(elMin == null) throw new NoSuchElementException();
Como en el caso del máximo el orden no debe preocuparse por los valores nulos. O en la variante de reforzar el filtro:
Iterable<T> agregado = ...; Ordering<T2> ord = ...; T2 elMin = null;
Boolean esElPrimero = true; for (T e : agregado) {
19 5. Tratamientos secuenciales
if (Filtro) { if(esElPrimero){ elMin = Expresion; esElPrimero = false; } else if (ord.compare(Expresion,elMax) < 0 ) { elMin = Expresion; } } } if(elMin == null) throw new NoSuchElementException();
Y de forma similar cuando el tipo T2 se instancia a algunos tipos particulares (como Integer, Double, etc.):
Iterable<T> agregado = ...; Integer elMin = Integer.MAX_VALUE;
for (T e : agregado) { if (Filtro && Expresion < elMax) { elMax = Expresion; } } if(elMax == Integer.MAX_VALUE) throw new NoSuchElementException();
O usando la técnica de tratar específicamente el primer elemento y sin asumir un valor que sea menor que todos los del tipo:
Iterable<T> agregado = ...; Ordering<T2> ord = ...; Integer elMax = null; Boolean esElPrimero = true;
for (T e : agregado) { if (Filtro) { if(esElPrimero){ elMax = Expresion; esElPrimero = false; } else if (Expresion < elMax) { elMax = Expresion; } } } if(elMax == null) throw new NoSuchElementException();
2.14 Iteración Genérica
• Objetivo: Acumular los valores calculados por la expresión sobre los elementos que cumplen el filtro según la operación contenida en un objeto de tipo Acumulator.Este objeto, con el tipo que se explica abajo, define una operación para acumular y un valor inicial para los valores a acumular.
• Tipo del acumulador: Acumulator<T>Valor Inicial: El establecido en el objeto de tipo Acumulator
• Expresión acumuladora: El acumulador más la expresión. • Operador binario: El definido en el objeto de tipo Acumulator
20 Introducción a la Programación
• Invariante: La variable acum contiene el resultado de acumular sumar los valores de la Expresión calculados sobre aquellos elementos recorridos hasta ese momento que cumplen la condición de Filtro.
• Función de Cota: el número de elementos del agregado que faltan por recorrer. • Código:
Iterable<T> agregado = ...; Acumulator<T2> acum =...; acum.setInitial();
for (T e : agregado) { if (Filtro) { acum.add(Expresion); if(acum.isDone()) break; } } T2 r = acum.getValue(); if(r == null) throw new NoSuchElementException();
Filtro es una expresión sobre la variable e que devuelve boolean. Esta expresión que hace el papel de Filtro debe tener en cuenta en su caso la posibilidad de que algún valor sea null como se ha explicado más arriba. Expresion es una expresión sobre la variable e que devuelveT2. El resultado, si existe, queda en la variable r.
En el código anterior hemos usado el tipo Acumulator<T>. Este tipo viene definido por la interfaz:
public interface Acumulator<T> { void setInitial(); void add(T e); T getValue(); boolean isDone(); }
El tipo Acumulator<T> ofrece los siguientes métodos:
• setInitial(): Situa el acumulador en su estado incial • add(T e): Acumula el valor e. • getValue(): Devuelve el valor acumulado • isDone(): Verdadero si podemos ya conocer el valor final sin recorrer todo el agregado.
Asumimos que si el valor devuelto por el acumulador es null es que nos e ha encontrado un valor válido.
Este esquema engloba a todos los anteriores. Casos particulares pueden ser acumuladores para calcular la suma de un Iterable de Racionales. En este caso la implementación del acumulador sería:
public class SumaRacional implements Acumulator<Racional> {
21 5. Tratamientos secuenciales
private Racional r = null; public SumaRacional() { } @Override public void setInitial()() { r = Racionales.create(); } @Override public void add(Racional e) { r.suma(e); } @Override public Racional getValue() {return r; } @Override public boolean isDone()() { return false; } }
Otro caso particular puede ser un acumulador para calcular el máximo según un orden:
public class Maximo<T> implements Acumulator<T> { private Racional r = null; private Comparator<> ord = null; private boolean esPrimero = true; private elMax = null; public Maximo(Comparator<> ord) { this.ord = ord; } @Override public void setInitial()() { esPrimero = true; } @Override public void add(T e) { if(esPrimero){ elMax = e; esPrimero = false; } else if(ord.compare(e,elMax) > 0) { elMax = e; } } @Override public T getValue() {return r; } @Override public boolean isDone()() { return false; } }
Igualmente podemos diseñar acumuladores para el resto de los esquemas: mínimo, buscar, existe, para todo, etc. Dejamos su implementación como ejercicio. Combinando la iteración genérica y un acumulador específico podemos obtener esquemas para caso concretos. En el caso anterior de la suma de Racionales:
22 Introducción a la Programación
Iterable<Racional> agregado = ...; Racional acum = Racionales.create();
for (T e : agregado) { if (Filtro) { acum.suma(e); } }
El resultado queda en la variable acum. Este tratamiento genérico puede combinarse con un tratamiento específico para el primer elemento si fuera necesario.
2.15 Algoritmos para grupos consecutivos de elementos:
Podemos diseñar también esquemas secuenciales para grupos consecutivos de elementos. Son generalizaciones de los anteriores donde el Filtro y la Expresion dependen ahora de varias variables.
Solo presentamos la generalización de Para Todo, Existe y Contar, para grupos de dos elementos consecutivos, sin recordar todos los detalles de los mismos. La generalización a más elementos consecutivos puede hacerse fácilmente. Igual podemos decir del resto de losm esquemas:
Contar Dos:
Iterable<T> agregado = ...; Boolean r = true; T elAnterior = null; Integer num = 0; Boolean esPrimero = true; for (T e : agregado) { if (Filtro2(elAnterior,e)) { if(esPrimero){ elAnterior = e; esPrimero = false; } else { num++; elAnterior = e; } } }
El resultado queda en la variable num.
Para Todo Dos:
Iterable<T> agregado = ...; Boolean r = true; T elAnterior = null; Boolean esPrimero = true; for (T e : agregado) { if (Filtro2(elAnterior,e)) {
23 5. Tratamientos secuenciales
if(esPrimero){ elAnterior = e; esPrimero = false; } else { r = r && Expresion2(elAnterior,e);
elAnterior = e; if(!r) break; } } }
El resultado queda en la variable r. Expresion2 devuelve un valor lógico.
Existe Dos:
Iterable<T> agregado = ...; Boolean r = false; T elAnterior = null; Boolean esPrimero = true; for (T e : agregado) { if (Filtro2(elAnterior,e)) { if(esPrimero){ elAnterior = e; esPrimero = false; } else { r = r || Expresion2(elAnterior,e);
elAnterior = e; if(r) break; } } }
El resultado queda en la variable r. Expresion2 devuelve un valor lógico.
Casos particulares de los esquemas anteriores son:
• Comprobar si un iterable está ordenado o estrictamente ordenado. En este caso Expresion2(elAnterior,e) se sustituirá (en el esquema Para Todo Dos) por la expresión que sea verdadera si elAnterior es menor o igual o menor que e (posiblemente con respecto a un orden). El Filtro2 será true.
• Comprobar si existen dos elementos iguales consecutivos. • Contar cuantos pares de elementos consecutivos hay que cumplan una propiedad
dada. 3. Ejemplos y Metodología de trabajo Los esquemas anteriores se repiten muy frecuentemente. Para poder usarlos debemos identificar en primer lugar cada uno de sus elementos:
24 Introducción a la Programación
• ¿Cuál es el agregado y su tipo (Iterable, array, secuencia)? Es simple o complejo? • ¿Cuál es el filtro si lo hay? • ¿Cuál es la expresión a acumular (o alternativamente la acción a llevar a cabo sobre
cada elemento)? • ¿Hay que tener en cuenta algún orden? • ¿Cuál de los esquemas usar?
Para los ejemplos siguientes suponemos implementados los tipos propuestos como ejercicios en el tema anterior. Ejemplo: Si queremos diseñar un método static de la clase Personas, tomando como parámetro una lista de Persona y una edad que debe ser positiva, devuelva como resultado la suma de las edades de las personas contenidas en la lista que sean mayores que al edad dada. No hay garantías de que los objetos en la lista sean distintos de null. En este caso tenemos:
• Origen de datos: la lista • Filtro: la persona es mayor que la edad proporcionada • Expresión: calcula la edad de una persona • Orden : no hay ninguno involucrado • Esquema a usar: suma
El código resultante, donde puede verse el código concreto del Filtro y de la Expresión, es:
public static Integer sumaEdades(List<Persona> lista, Integer ed){ if(ed <= 0){ throw new IllegalArgumentException(); } Integer suma = 0; for (Persona e : lista) { if (e!=null && e.getEdad() > ed) { suma = suma + e.getEdad(); } } return suma; }
Los siguientes ejemplos ilustran algunos de los esquemas secuenciales. Todos los métodos los podemos ubicar en la clase Racionales. Vamos viendo el enunciado, el código y algunos comentarios. Ejemplo Dado un array de String cada una de las cuales representa un racional, obtener una lista de racionales.
25 5. Tratamientos secuenciales
public static List<Racional> getLista (String[] ag){
List<Racional> lr = new ArrayList(); for (String e : ag) { lr.add(Racionales.create(e)); } return lr; }
Ahora hemos usado el esquema selecciona lista, no hay filtro y Racionales.create(e) es la Expresión que construye objetos de tipo Racional. Ejemplo Implementar el método getFotos en la clase Bibliotecas que dada una Biblioteca y dos fechas, la segunda mayor que la primera, devuelva todas las fotos distintas que contiene que fueron realizadas entre esas dos fechas.
public static Set<Foto> getFotos (Biblioteca b, Fecha f1, Fecha f2){ if(!(f1.compareTo(f2) <=0)){ throw new IllegalArgumentException(); }
Set<String> sr = new HashSet(); for (Album a : b) { for(Foto f: a.getFotos()){ Fecha ff = f.getFecha(); if(ff.compareTo(f1) > 0 && ff.compareTo(f2) < 0){ sr.add(f); } } } return sr; }
Ahora hemos usado el esquema selecciona conjunto, el filtro es que la fecha este estrictamente entre las dos proporcionadas. Suponemos que todas las fotos son distintas de null. El origen de datos tiene una lista de álbumes y cada álbum una lista de fotos. Ejemplo Dado un array de Racional cada una de las cuales representa un racional, obtener cuantos hay mayores que cero.
public static Integer getNumeroDePositivos (Racional[] ag){ Integer num = 0; Racional cero = Racionales.create();
for (Racionale : ag) { if(e!=null && e.compareTo(cero)){ num++;
}
26 Introducción a la Programación
} return num; }
Ahora hemos usado el esquema contar, el filtro es que el racional sea distinto de null y positivo. Vemos también que cuando una expresión tenemos que usarla repetidamente y siempre y sabemos que siempre devolverá el mismo valor es conveniente hacer el cálculo, guardar el resultado en una variable local y usar esta variable posteriormente. Ejemplo Dada una lista de Viaje calcular cuantas ciudades distintas pueden visitarse si hacen todos los viajes de la lista que sean posibles para un grupo formado por un número dado de personas.
public static Integer getCiudadesDistintas (List<Viaje> lista, Integer n){ if(n <= 0){ throw new IllegalArgumentException(); }
Set<String> sr = new HashSet(); for (Viaje e : ag) { if(e.getMinimoDePersonas() >= n){ for(String s: e.getCiudadesVisitadas(){ sr.add(s); } } } return sr.size(); }
Ahora hemos usado el esquema selecciona conjunto, el filtro es que el grupo sea mayor o igual al entero dado. El número de objetos distintos es el cardinal del conjunto construido. Podemos observar que el origen de datos está compuesto de una lista cada uno de cuyos objetos tiene una propiedad que otra lista. También es interesante constatar que el filtro depende de la variable de tipo Vuelo y no de la de tipo String. Esta es la razón para poner el if entre los dos for. Si el fitro hubiera dependido de la ciudad, de tipo String, entonces iría después del segundo for. Ejemplo Dada una lista de Polígonos diseñar un método static que nos devuelva el que tenga un área mayor de entre los que tengan un número de lados dado.
public static Poligono getMayorArea (List<Poligono> lista, int n){ Ordering<Double> ord = Ordering.from(new PorArea()); Ordering<Double> ord1 = ord.nullsFirst(); Poligono elMax = null;
for (Poligono p : lista) {
if (p!=null && p.getNumeroDeVertices() == n) { elMax = ord1.max(elMax,p); }
}
27 5. Tratamientos secuenciales
if(elMax == null) throw new NoSuchElementException(); return elMax; }
Necesitamos diseñar un orden sobre los polígonos: el dado por sus áreas.
public class PorArea implements Comparator<Poligono>{ public PorArea(){ } public int compare(Poligono p1, Poligono p2){ Integer r; r = p1.getArea().compareTo(p2.getArea()); return r; } }
Alternativamente puesto que podemos hacer un diseño específico para este caso sin tener que diseñar nuevos objetos de tipo Comparator usando la técnica de tratar específicamente el primer elemento.
public static Poligono getMayorArea (List<Poligono> lista, int n){ Poligono elMax = null; Double areaMax = Double.MIN_VALUE; Boolean esPrimero = true; for (Poligono p : lista) { if(p!=null && p.getNumeroDeVertices() == n){ if(esPrimero) { elMax = p; esPrimero = false; else if (p.getArea() > elMax.getArea()) { elMax = p; } } } if(elMax == null) throw new NoSuchElementException(); return elMax; }
Es conveniente explicitar por qué p.getArea() > elMax.getArea() es la expresión adecuada para implementar ord.compare(Expresion,elMax) > 0
Ejemplo Dada una lista de Polinomio diseñar un método static que nos devuelva una lista con sus derivadas.
public static List<Polinomio> getDerivadas (List<Polinomio> ag){ List<Polinomio> lr = new ArrayList();
for (Polinomio e : ag) {
lr.add(e.getDerivada());
}
28 Introducción a la Programación
return lr; }
Ejemplo Dado un Iterable de T diseñar un método static que nos devuelva un String formado por la representación en cadenas de caracteres de los elementos separadas por comas y entre corchetes. Usaremos un esquema similar a la iteración genérica con tratamiento específico del primer elemento.
public static <T> String getString (Iterable<T> ag){ String s “”; Boolean esPrimero = true;
for (T e : ag) { if(esPrimero){ s = “[“+e; esPrimero = false; } else { s = “,”+e; } } s = s+”]”; return s; }
Ejemplo Dada un Iterable de Complejo diseñar un método static que nos devuelva el producto de todos sus elementos. Podemos resolver con la Iteración General con el acumulador:
public class MultiplicaComplejo implements Acumulator<Complejo> { private Complejo r = null; public MultiplicaComplejo() { }
@Override public void setInitial()() { r = Complejos.create(1,0); } @Override public void add(Complejo e) { r.multiplica(e); }
@Override public Racional getValue() {return r; }
@Override
public boolean isDone()() { return false; }
29 5. Tratamientos secuenciales
}
Alternativamente podemos combinar la iteración general con el acumulador concreto para dar el esquema:
Iterable<Complejo> agregado = ...; Complejo acum = Complejos.create(1,0);
for (T e : agregado) { acum.multiplica(e); }
4. Conceptos aprendidos
• Operaciones habituales con agregados de datos • Elementos comunes a las operaciones habituales: acumulador, filtro y expresión • Esquemas secuenciales comunes
5. Ejercicios Propuestos
1. Dada una lista de números enteros, que se toma como parámetro, implementa un método static en la clase Enteros para cada una de las siguientes cuestiones:
a) ¿Todos los elementos de la lista son impares? b) ¿Existe alguno que sea impar y primo? Supóngase implementado previamente el
método esPrimo. c) ¿Cuántos hay? d) ¿Cuál es el valor de suma de los que sean primos? ¿Y del producto de los que sean
impares? e) ¿Hay alguno que divida a un número n (siendo n un parámetro del método)? f) Buscar todos los enteros pares que están en la lista de entrada (que el método
devuelva el resultado en otra lista)
2. Dado un Set<List<Integer>> que se toma como parámetro, implementa un método static en la clase Enteros para cada una de las siguientes cuestiones:
a) ¿Cuánto vale la suma de todos los elementos que son impares? b) ¿Cuál es el entero más pequeño? ¿y el más grande? c) ¿Existe algún elemento que sea primo? d) ¿Existe algún elemento que sea primo y que ocupe la primera posición de cada
lista del conjunto? e) Devolver un List<Integer> con todos aquellos enteros que ocupan la primera
posición en cada lista del conjunto.
30 Introducción a la Programación
3. Dada una lista de números racionales, que se toma como parámetro, implementa un método static en la clase Racionales para resolver cada una de las siguientes cuestiones:
a) ¿Cuál es el mayor? ¿y el menor? b) ¿Todos los elementos de la lista tienen valor mayor que 1? c) ¿Cuál es el valor de suma, en valor real, de los que sean menores que 1, en valor
real? d) ¿Y del producto, en valor real, de los que tengan el denominador mayor que 3, en
valor real? e) Realiza los dos apartados anteriores devolviendo un valor racional f) Cuantos racionales distintos hay?
4. Dado un List<Set<Racional>> que se toma como parámetro, implementa un método static en la clase Racionales para resolver cada una de las siguientes cuestiones:
a) ¿Cuál es el conjunto que tiene más elementos? b) ¿Existe algún conjunto con un número impar de elementos? c) Devolver un racional cuyo numerador sea la suma de los numeradores y cuyo
denominador el producto de los denominadores.
5. Dado un Set<List<Integer>> que se toma como parámetro, implementa un método static en la clase Enteros para resolver cada una de las siguientes cuestiones:
a) ¿Cuál es la lista de mayor tamaño? b) ¿Existe alguna lista que contenga algún entero con valor negativo? c) ¿Todas las listas son de tamaño mayor que 2? d) ¿Cuánto suma la lista de todos los números enteros?
6. En una biblioteca de fotos digitales, con los tipos definidos en el capítulo anterior,
implemente los siguientes métodos : a) El método getTamaño de la clase Albumes, que devuelve el tamaño total de las fotos
de un álbum, que toma como parámetro, como la suma de los tamaños de todas las fotos del álbum.
b) Un método estático de la clase Bibliotecas que devuelva un álbum con las fotos de una biblioteca que tienen un descriptor dado y han sido retocadas. El nombre del álbum será el propio descriptor.
7. Usando los tipos del capítulo anterior implemente:
a) Un método llamado beneficioTotal que tome como argumentos un Huerto y un Integer, y devuelva el beneficio total del huerto de aquellas cosechas cuyo tiempo de recolección sea menor al entero recibido.
b) Un método llamado aumentaBeneficios, que reciba un Huerto, un Set <String> y un Double y aumente en esa cantidad el beneficio de aquellas cosechas cuyo nombre esté contenido en el conjunto.
8. Implemente un método estático en la clase LectoresElectrónicos para responder a cada
una de las siguientes preguntas. a) Un método que construya un Set<Libro> con todos los libros de un
LectorElectrónico<Libro> que contengan el descriptor “programación” y tengan una valoración igual o superior a 4.
31 5. Tratamientos secuenciales
9. Implemente los siguientes métodos en la clase Viajes:
a) Un método que devuelva la calificación media de los clientes para todos los viajes en el catálogo. Parámetros de entrada: catálogo de viajes.
b) Un método que diga si todos los viajes del catálogo son de un operador determinado. Parámetros de entrada: catálogo de viajes y nombre del operador.
10. Dadas las siguientes interfaces:
public interface Destino extends Comparable<Destino>{ Double getDistancia(); String getNombre();
} public interface Vuelo {
Destino getDestino(); Double getPrecio(); Integer getNumPlazas(); Integer getNumPasajeros(); Integer getCodigo(); Fecha getFechaSalida(); Persona getPiloto();
} public interface CompañiaAerea {
Integer getCodigo(); String getNombre(); Vuelo[] getVuelos();
}
Implemente los siguientes métodos en la clase Compañias:
a) Un método que dada una compañía aérea devuelva el nombre del piloto del primer vuelo (de salida más pronto) con plazas libres con destino París.
b) Construya un método que dada una compañía aérea devuelva el código del vuelo que más distancia recorre de entre los que salen hoy.