Fundamentos de Programación - us

16
FUNDAMENTOS DE PROGRAMACIÓN Versión: 0.0.7 Tema 8. Iterables: Implementación Autor: Miguel Toro Bonilla Revisión: Jorge García Gutiérrez, Antonia M. Reina Tiempo estimado: 4 horas 1. Introducción ................................................................................................................................................ 1 2. Iterables e iteradores.................................................................................................................................. 1 3. Iterables Virtuales (ejemplos) .................................................................................................................... 4 4. Iterables basados en iterables (ejemplos) ................................................................................................. 7 5. Lectura de ficheros: Scanner e Iterables2.fromFile ................................................................................. 12 6. Ejercicios.................................................................................................................................................... 14 1. Introducción En temas anteriores, vimos que es muy importante la reutilización para conseguir un software de calidad. Esta reutilización es, también, muy importante en el diseño de las soluciones a los problemas, ya que muchas de ellas siguen un mismo patrón. Los patrones de software son soluciones probadas para determinados tipos de problemas que se presentan a menudo en distintos contextos, pero con características similares. Estos patrones de software tienen muchas ventajas: Están probados, porque han sido usados en múltiples problemas anteriormente. Son reutilizables, porque los problemas que resuelven aparecen repetidos múltiples veces en las aplicaciones. Son expresivos, porque establecen unos conceptos y, en muchos casos, una notación, que permite plantear la solución a un problema concreto como un caso particular de una solución general. La factoría de un tipo, vista ya en temas anteriores, es un patrón de diseño. El patrón que presentamos en este tema y, cuyo uso está muy extendido, es el patrón Iterator. Además, hablaremos del tipo Iterable que define objetos que actúan como factorías de objetos Iterator. 2. Iterables e iteradores Una tarea habitual en programación es realizar recorridos sobre todos los elementos de un agregado de objetos y hacer diversos tratamientos con ellos (por ejemplo, los vistos en el tema sobre tratamientos secuenciales). Para poder reutilizar el código de los diversos tratamientos es necesario que todos los agregados de datos sean vistos desde el exterior de forma similar. Es decir, que todos los agregados de datos ofrezcan el mismo contrato a sus posibles clientes. La solución a este problema es que cada agregado de

Transcript of Fundamentos de Programación - us

Page 1: Fundamentos de Programación - us

FUNDAMENTOS DE PROGRAMACIÓN

Versión: 0.0.7

Tema 8. Iterables: Implementación Autor: Miguel Toro Bonilla

Revisión: Jorge García Gutiérrez, Antonia M. Reina

Tiempo estimado: 4 horas

1. Introducción ................................................................................................................................................ 1

2. Iterables e iteradores .................................................................................................................................. 1

3. Iterables Virtuales (ejemplos) .................................................................................................................... 4

4. Iterables basados en iterables (ejemplos) ................................................................................................. 7

5. Lectura de ficheros: Scanner e Iterables2.fromFile ................................................................................. 12

6. Ejercicios .................................................................................................................................................... 14

1. Introducción

En temas anteriores, vimos que es muy importante la reutilización para conseguir un software de calidad.

Esta reutilización es, también, muy importante en el diseño de las soluciones a los problemas, ya que

muchas de ellas siguen un mismo patrón.

Los patrones de software son soluciones probadas para determinados tipos de problemas que se

presentan a menudo en distintos contextos, pero con características similares. Estos patrones de software

tienen muchas ventajas:

Están probados, porque han sido usados en múltiples problemas anteriormente.

Son reutilizables, porque los problemas que resuelven aparecen repetidos múltiples veces en las

aplicaciones.

Son expresivos, porque establecen unos conceptos y, en muchos casos, una notación, que permite

plantear la solución a un problema concreto como un caso particular de una solución general.

La factoría de un tipo, vista ya en temas anteriores, es un patrón de diseño. El patrón que presentamos en

este tema y, cuyo uso está muy extendido, es el patrón Iterator. Además, hablaremos del tipo Iterable que

define objetos que actúan como factorías de objetos Iterator.

2. Iterables e iteradores

Una tarea habitual en programación es realizar recorridos sobre todos los elementos de un agregado de

objetos y hacer diversos tratamientos con ellos (por ejemplo, los vistos en el tema sobre tratamientos

secuenciales). Para poder reutilizar el código de los diversos tratamientos es necesario que todos los

agregados de datos sean vistos desde el exterior de forma similar. Es decir, que todos los agregados de datos

ofrezcan el mismo contrato a sus posibles clientes. La solución a este problema es que cada agregado de

Page 2: Fundamentos de Programación - us

2 Fundamentos de Programación

datos implemente el contrato Iterable. En seguida veremos los detalles de este contrato, pero antes,

veamos las ventajas que ofrece.

En primer lugar, si un agregado de datos ag implementa el contrato Iterable<T> entonces el agregado puede

ser recorrido mediante un for extendido.

for (T e : ag) {

mostrar(e);

}

El código anterior es el mismo para cualquier agregado que implemente Iterable<T> y, como hemos visto en

temas anteriores, la sentencia for recorre cada elemento del agregado y termina cuando los haya recorrido

todos. En cualquier caso, para que un agregado de datos pueda recorrerse mediante un for extendido debe

ofrecer la interfaz Iterable (con la excepción de los arrays, como ya comentamos anteriormente).

El tipo Iterable

El tipo Iterable<T> es una factoría de objetos de tipo Iterator<T>. El tipo Iterable<T> viene definido por la

interfaz:

public interface Iterable<T> { Iterator<T> iterator(); }

El método iterator() devuelve un iterador que describe cómo se recorre el agregado que implementa este

tipo Iterable.

El tipo Iterator

Cada agregado de datos puede tener diversas formas de recorrer los objetos que lo componen. Un objeto

que implementa el tipo Iterator<T> tiene como responsabilidad gestionar un recorrido concreto de un

agregado. Es decir, debe proporcionar cada uno de los elementos del recorrido de forma consecutiva y en el

orden especificado por el mismo.

Por cada recorrido de interés, el agregado debe implementar un iterador (un objeto que implementa el tipo

Iterator<T>). El tipo Iterator<T> viene definido en la interfaz:

public interface Iterator<T> { boolean hasNext(); T next(); void remove(); }

Page 3: Fundamentos de Programación - us

3 8. Iterables: Implementación

Como hemos indicado, los objetos que implementan este tipo (iteradores) tienen como responsabilidad

proporcionar un primer elemento del recorrido que tienen asociado y, posteriormente, los siguientes

elementos del mismo. Además, en cada momento deben indicarnos si quedan más objetos por recorrer o

no. El significado de los métodos de la interfaz está íntimamente relacionado con estas necesidades:

boolean hasNext(): Devuelve true si aún quedan elementos por recorrer y false en caso contrario.

T next(): La primera vez que se le llama devuelve el primer elemento. En cada llamada posterior

devuelve el siguiente elemento del recorrido. Este método tiene como precondición que hasNext()

devuelva true. Si no se cumple la precondición, entonces elevará la excepción NoSuchElementException.

void remove(): Elimina del agregado el último elemento devuelto por el método next.

En general, definiremos iteradores que implementen recorridos puros que no modifiquen el agregado. Los

objetos de este tipo son objetos del tipo Iterator<T> que no tienen disponible la operación remove. Si se

invoca esta operación, se elevará la excepción UnsupportedOperationException.

Funcionamiento del tipo Iterable

Para comprender el funcionamiento del tipo Iterable<T> veamos la forma de traducir una sentencia for

extendido a otra con for clásico o while (ejemplo 1). Supongamos que el objeto ag implementa Iterable<T>.

Entonces los tres segmentos de código (con for extendido, for clásico y while) son equivalentes. De hecho, un

for extendido es siempre desplegado a su versión clásica en una etapa previa al compilado del código.

Teniendo esto en cuenta podemos ver que la sentencia de control for extendido obtiene un iterador del

agregado en primer lugar, que al implementar Iterable<T> es una factoría de los mismos. Usando el iterador,

el bucle for entra en una secuencia de iteraciones mientras que el método hasNext devuelva true. En cada

iteración obtiene el elemento siguiente llamando al método next del iterador.

Ejemplo 1:

for (T e : ag) {

mostrar(e);

}

for (Iterator<T> it = ag.iterator();

it.hasNext(); ) {

T e = it.next();

mostrar(e);

}

Iterator<T> it = ag.iterator();

while (it.hasNext()) {

T e = it.next();

mostrar(e);

}

Como vemos en la sentencia con el for extendido, el iterador se usa internamente, pero no se muestra

públicamente. Es por esto que si un agregado quiere participar en un for extendido, debe implementar el

tipo Iterable<T>. Como último comentario, se debe tener en cuenta que siempre se crea un iterador nuevo

al inicio de cada sentencia for extendido.

Pasos para implementar un objeto Iterable

Para implementar un tipo de objeto que sea Iterable<T>, debemos dar los siguientes pasos:

1. Definir una clase que implemente la interfaz Iterable<T>, siendo T el tipo de los elementos que se

van a recorrer.

Page 4: Fundamentos de Programación - us

4 Fundamentos de Programación

2. Para cada tipo de recorrido que queramos permitir, crearemos una clase interna, la cual debe

implementar Iterator<T>. Igual que dentro de una clase podemos declarar un atributo o un método,

también podemos incluir otra clase u otra interfaz. A las clases incluidas dentro de otras se las

denomina clases internas y pueden tener visibilidad pública o privada.

3. Cada posible iterador debe tener un estado para mantener la posición actual del recorrido. Ese

estado se concretará en atributos de la clase interna correspondiente. El constructor de la misma

debe inicializar el estado del iterador.

4. Los métodos hasNext() y next() de dicha clase interna deben implementarse de acuerdo al tipo de

recorrido que se esté llevando a cabo. El método hasNext(), como hemos visto anteriormente, es un

método observador que nos indica si hay, o no, más elementos en el agregado. El método next()

tiene como precondición que hasNext() sea verdadero. Si se cumple que existe elemento disponible,

devuelve dicho elemento del agregado y prepara el estado del iterador para poder devolver el

siguiente.

5. El método remove() contendrá el código adecuado para eliminar del agregado el último elemento

devuelto por el método next(). Si no queremos permitir el uso de este método el cuerpo del mismo

elevará la excepción UnsupportedOperationException.

6. Finalmente, definiremos el método iterator() en la clase contenedora. Dicho método devolverá un

nuevo objeto de la clase interna oportuna. Si hay varios recorridos posibles diseñaremos varias

clases internas. En todo caso, el método iterator() se diseñará como una factoría para devolver un

objeto creado desde alguna de las clases internas.

3. Iterables Virtuales (ejemplos)

En este apartado nos centraremos en definir como ejemplos algunos iterables a los que llamaremos

agregados virtuales que permitan recorrer secuencias virtuales de elementos. Es decir, agregados que no

están en la memoria del ordenador y que devuelven los valores generados en cada iteración.

SecuenciaAritmetica

Iterable que permite recorrer los números de un intervalo con un incremento determinado.

Constructor:

public SecuenciaAritmetica(Double vi, Double vf, Double inc)

Ejemplo de uso:

Iterable<Double> sec = new SecuenciaAritmetica(1.,30.,5.);

for (Double d : sec) {

mostrar(d);

}

Resultado del código de ejemplo:

Page 5: Fundamentos de Programación - us

5 8. Iterables: Implementación

1.0

6.0

11.0

16.0

21.0

26.0

La implementación sigue una estructura que repetiremos en los siguientes iterables:

o Definimos una variable privada en la clase interna. Esta variable mantiene el estado del iterador.

La variable guarda el siguiente valor a devolver por el método next o un valor que está fuera del

rango permitido. En algunos casos es necesario definir varias variables privadas para mantener

el estado el iterador.

o El método hasNext simplemente comprueba si el valor de la variable está dentro del rango.

o El método next guarda el valor de la variable, calcula el siguiente actualizando la misma, y

devuelve el valor antiguo.

public class SecuenciaAritmetica implements Iterable<Double> { private Double primero,ultimo,incremento; public SecuenciaAritmetica(Double a, Double b, Double c) { if( b < a || c <= 0 ) { throw new IllegalArgumentException( “SecuenciaAritmetica.SecuenciaAritmetica::Rango no válido”); } primero = a; ultimo = b; incremento= c; } public SecuenciaAritmetica(Double a, Double b) { this(a,b,1.); } public Iterator<Double> iterator() { return new IteradorSecuenciaAritmetica(); } private class IteradorSecuenciaAritmetica implements Iterator<Double> { private Double actual; public IteradorSecuenciaAritmetica() { actual = primero; } public Double next() { if(!hasNext()) throw new NoSuchElementException(); Double r = actual; actual = actual + incremento; return r; }

Page 6: Fundamentos de Programación - us

6 Fundamentos de Programación

public boolean hasNext() { return actual < ultimo; } public void remove() { throw new UnsupportedOperationException(); } } }

Secuencia de Pares

Es un iterable para generar pares de números enteros donde la primera componente del par estará formada

por los enteros desde ma1 (incluido) hasta ma2 (no incluido) y la segunda componente formada por los

enteros desde mb1 (incluido) hasta mb2 (no incluido).

Constructor:

public SecuenciaDePares(Integer ma1, Integer ma2, Integer mb1, Integer mb2)

Ejemplo de uso:

Iterable<Par<Integer>> it = new SecuenciaDePares(1, 3, 2, 5); for (Par<Integer> p : it) { mostrar(p); }

Resultado del código de ejemplo:

(1,2)

(1,3)

(1,4)

(2,2)

(2,3)

(2,4)

El tipo Par representa un par de elementos y su diseño ya se ha visto con anterioridad. La

implementación del iterable sigue el esquema visto en el anterior ejercicio.

public class SecuenciaDePares implements Iterable<Par<Integer>> { private Integer a1; private Integer a2; private Integer b1; private Integer b2; public SecuenciaDePares(Integer a1, Integer a2, Integer b1, Integer b2) { if (a1>a2 || b1>b2) throw new IllegalArgumentException( “SecuenciaDePares.SecuenciaDePares::Rango no válido”); this.a1 = a1; this.a2 = a2;

Page 7: Fundamentos de Programación - us

7 8. Iterables: Implementación

this.b1 = b1; this.b2 = b2; } public Iterator<Par<Integer>> iterator() { return new IteradorDePares(); } private class IteradorDePares implements Iterator<Par<Integer>> { private Integer actualA; private Integer actualB; public IteradorDePares() { actualA = a1; actualB = b1; } public boolean hasNext() { return actualA < a2; } public Par<Integer> next() { if (!hasNext()) throw new NoSuchElementException(); Par<Integer> res = new ParImpl<Integer>(actualA, actualB); if (actualB < b2 - 1) { actualB++; } else { actualA++; actualB = b1; } return res; } public void remove() { throw new UnsupportedOperationException(); } } }

4. Iterables basados en iterables (ejemplos)

Para finalizar esta parte del temario, proporcionamos otros ejemplos de iterables que se basan en

información contenida en memoria, disco,... En este caso, los objetos iterables suelen usarse para

proporcionar maneras adicionales de acceder a la información.

Iterable de array

Se trata de hacer iterable un objeto de tipo array. Este tipo de objetos no es directamente iterable en Java,

aunque sí se puede recorrer mediante un for extendido. Para este ejemplo, vamos a permitir dos tipos de

recorridos: desde la posición 0 hasta la n-1 y la opuesta. Para completar el tipo, definiremos una interfaz

ArrayIterable que extiende Iterable y que tiene un solo método: setOpcion. Por lo demás, el tipo sigue el

patrón anteriormente visto.

Page 8: Fundamentos de Programación - us

8 Fundamentos de Programación

Constructor:

public ArrayIterableImpl (T[] a) {…}

Ejemplo de uso:

Integer [] a = {3,4,-7,23};

ArrayIterable<Integer> v = new ArrayIterableImpl<Integer>(a);

for (Integer p : v) {

mostrar(p);

}

v.setOpcion(Recorrido.HACIA_ATRAS);

for (Integer p : v) {

mostrar(p);

}

Resultado del código de ejemplo:

3

4

-7

23

23

-7

4

3

Implementación:

La implementación para este tipo de ejercicios se basa en el control mediante una variable índice de la

posición del elemento a devolver en cada iteración. El control del tipo de recorrido se realiza mediante el

método setOpcion que actualiza un atributo privado que define la forma de recorrer el objeto. Para cada

tipo de recorrido tendremos una clase interna y el método iterator se diseñará como una factoría que

selecciona el iterador correcto en cada caso.

public interface ArrayIterable<T> extends Iterable<T> { T[] getArray(); void setOpcion(Recorrido r) Recorrido getOpcion(); } public class ArrayIterableImpl<T> implements Iterable<T> { public enum Recorrido {

HACIA_ADELANTE, HACIA_ATRAS }

Page 9: Fundamentos de Programación - us

9 8. Iterables: Implementación

private T[] array; private Recorrido opcion = Recorrido.HACIA_ADELANTE; public ArrayIterableImpl(T[] array) { if(array == null){ throw new IllegalArgumentException(); } this.array = array; } public Iterator<T> iterator() { Iterator<T> it = null; switch (opcion) { case HACIA_ADELANTE: it = new IteratorAdelante(); break; case HACIA_ATRAS: it = new IteratorAtras(); break; } return it; }

public T[] getArray() { return array; } public Recorrido getOpcion() { return opcion; } public void setOpcion(Recorrido opcion) { this.opcion = opcion; } private class IteratorAdelante implements Iterator<T> { private int actual; public IteratorAdelante() { actual = 0; }

public T next() { if (!hasNext()) throw new NoSuchElementException(); int r = actual; actual = actual + 1; return array[r]; } public boolean hasNext() { return actual < array.length; } public void remove() { throw new UnsupportedOperationException(); } }

Page 10: Fundamentos de Programación - us

10 Fundamentos de Programación

private class IteratorAtras implements Iterator<T> { private int actual; public IteratorAtras() { actual = array.length-1; } public T next() { if (!hasNext()) throw new NoSuchElementException(); int r = actual; actual = actual - 1; return array[r]; } public boolean hasNext() { return actual >= 0; } public void remove() { throw new UnsupportedOperationException(); } } }

Iterable Flujo Entrada

Esta clase nos permitirá leer datos desde un fichero de texto o desde el teclado convirtiéndolo en un

Iterable<String>.

Constructor:

public FlujoEntrada(String nombreFich)

Ejemplo de uso:

Iterable<String> it = new FlujoEntrada(“Puntos.txt”); for(String s: it) { mostrar(s); }

Resultado del código de ejemplo:

Suponiendo que el fichero de nombre Puntos.txt es de la forma:

(2.0,3.1)

(2.2,3.1)

(3.2,3.0)

(4.1,3.2)

(2.0,3.2)

(2.1,3.2)

Page 11: Fundamentos de Programación - us

11 8. Iterables: Implementación

Entonces cada uno de los objetos en it son las líneas de ese fichero y en el ejemplo anterior lo que haríamos

sería mostrar las mismas líneas por consola.

Implementación:

La implementación usa clases del paquete java.io y java.util. Estas clases son File y Scanner, cuyos detalles

pueden verse en la documentación de la API de Java y que brevemente se verán en la siguiente sección. En

cualquier caso, es importante tener en cuenta que Scanner implementa Iterator<String> y devuelve cadenas

(entre otras posibilidades) leídas desde la entrada estándar o desde ficheros (los objetos tipo File

representan los ficheros en Java). Por lo tanto, podemos reutilizar dicha clase para resolver nuestro iterador

y, de esta forma, completar nuestro objeto iterable FlujoEntrada.

Además, el constructor de Scanner eleva excepciones que debemos gestionar. En nuestro caso, hemos

decidido convertir esas excepciones en IllegalStateException para indicar que el fichero al que se intenta

acceder no es válido en el momento en el que se intenta iterar. Tenga en cuenta que usar

FileNotFoundException directamente, implicaría forzosamente cambiar el prototipo del método iterator

(cosa que no podemos hacer puesto que es un método de Iterable) ya que se trata de una excepción

comprobable (no hereda de RuntimeException).

public class FlujoEntrada implements Iterable<String> { private String nf; private String delimiter; public FlujoEntrada(String nombreFich) { nf = nombreFich; delimiter = null; } public FlujoEntrada(String nombreFich, String delim) { nf = nombreFich; delimiter = delim; } public Iterator<String> iterator() { Iterator<String> itor = null; try { if (delimiter!=null) { itor = new Scanner(new File(nf)).useDelimiter(delimiter); } else { itor = new IteradorFicheroLineas(); } } catch (FileNotFoundException e) { throw new IllegalStateException(); } return itor; }

Page 12: Fundamentos de Programación - us

12 Fundamentos de Programación

private class IteradorFicheroLineas implements Iterator<String> { private Scanner sc; public IteradorFicheroLineas() { try { sc = new Scanner(new File(nf)); } catch (FileNotFoundException e) { throw new IllegalStateException(); } } public void remove() { throw new UnsupportedOperationException(); } public boolean hasNext() { return sc.hasNextLine(); } public String next() { return sc.nextLine(); } } }

5. Lectura de ficheros: Scanner e Iterables2.fromFile

La clase Scanner

Como se ha visto antes, Java proporciona en el paquete java.util una clase que se denomina Scanner y que

permite leer datos desde ficheros de texto o incluso desde teclado. Los objetos de tipo Scanner, mediante la

invocación de distintos métodos, permiten leer datos de cualquier tipo (String, enteros, reales, etc.) con

diversas posibilidades de separadores.

Para construir un objeto de tipo Scanner se invocará al constructor de la clase pasándole como argumento

un objeto de tipo File (que se encuentra en el paquete java.io). Los objetos de tipo File relacionan un fichero

con su nombre y ruta en el sistema de archivos del ordenador. Un objeto de tipo File se crea mediante un

constructor al que se le pasa como argumento una cadena de caracteres con el nombre y la ruta del fichero

que se quiere leer o escribir:

File f = new File("palabras.txt");

La anterior línea indica que el fichero palabras.txt se encuentra en el directorio raíz de la carpeta que

contenga nuestro proyecto Java. Si quisiéramos el fichero que esté en una carpeta concreta:

File f = new File("c:\Usuarios\pedro\clases\poo\tema6\palabras.txt");

Una vez creado el fichero f se puede invocar al constructor de la clase Scanner:

Page 13: Fundamentos de Programación - us

13 8. Iterables: Implementación

Scanner sc = new Scanner(f);

Sin embargo, lo usual es hacerlo todo en la misma sentencia ya que el objeto de tipo File normalmente no se

va a reutilizar:

Scanner sc = new Scanner(new File("palabras.txt"));

La clase Scanner proporciona un conjunto de métodos para leer el contenido del fichero de texto que se ha

conectado mediante la invocación del constructor. En la siguiente tabla se exponen algunos de los más

interesantes; como siempre, se puede ver la relación completa en la documentación de Java1.

Boolean hasNext()

Returns true if this scanner has another token in its input.

String next()

Finds and returns the next complete token from this scanner.

Scanner useDelimiter(String pattern)

Sets this scanner's delimiting pattern to a pattern constructed from the specified String.

El método useDelimiter sobre un objeto Scanner lleva como argumento un objeto de tipo Pattern2, que es

una representación como cadena de una expresión regular3. En esta asignatura sólo lo vamos a usar para

indicar separadores distintos del espacio en blanco, que es el separador por defecto de la clase Scanner (por

ejemplo para leer línea a línea podemos utilizar la expresión regular “\\n+$”). Es importante recordar que

Scanner no elimina los blancos por lo que puede ser interesante usar el método trim() de String para

eliminar blancos, tabuladores,… para cada uno de los elementos que se devuelvan. En cualquier caso, el

resultado final de la llamada a useDelimiter() es un nuevo objeto Scanner distinto del que ha invocado el

método con delimitadores personalizados.

Encapsulación de la lectura: La clase Iterables2 y el método fromFile

Los trozos de código de la sección anterior no están situados en ningún método en concreto. Como ya

sabemos de temas anteriores, la programación orientada a objetos debe encapsular las líneas de código de

una determinada acción en un método, bien sea un método de una clase, bien sea un método de una clase

de utilidad. En el caso de la lectura de ficheros, aprovecharemos el tipo FlujoEntrada para encapsular la

definición del objeto Scanner y, de esta forma, a partir de un fichero, podemos obtener un Iterable<String>

que permita recorrer las líneas de dicho fichero.

La clase en la que iremos definiendo los métodos de utilidades para objetos iterables, se denominará

Iterables2 (veremos más adelante el porqué del uso del número en su nombre). Y para empezar,

definiremos dentro de ella el método fromFile de la siguiente manera:

1 http://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html

2 http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html

3 http://es.wikipedia.org/wiki/Expresi%C3%B3n_regular

Page 14: Fundamentos de Programación - us

14 Fundamentos de Programación

public class Iterables2 { public static Iterable<String> fromFile(String file){ return new FlujoEntrada(file); } }

Para utilizar la clase de utilidad, podríamos definir una clase TestLectura como sigue: public static void main(String[] args) { Iterable<String> itbl = Iterables2.fromFile("Puntos.txt"); for (String s : itbl) { mostrar(s); } }

Donde la salida nos daría algo como:

(2.0,3.1)

(2.2,3.1)

(3.2,3.0)

(4.1,3.2)

(2.0,3.2)

(2.1,3.2)

6. Ejercicios

1. Cree una clase denominada SecuenciaGeométrica que implemente Iterable<Double>. Los objetos

SecuenciaGeométrica se recorrerán mediante un iterador, teniendo en cuenta que cada valor devuelto

es un número entre dos valores dados que se recogerán como parámetros en el constructor.

o Constructor:

public SecuenciaGeometrica (Double inicial, Double fin, Double razon)

o Ejemplo de uso:

Iterable<Double> it = new SecuenciaGeometrica(1.0, 1000.0, 5.0); for(Double d: it) {

mostrar(d); }

o Resultado del código de ejemplo:

1.0

5.0

25.0

125.0

625.0

Page 15: Fundamentos de Programación - us

15 8. Iterables: Implementación

2. En este ejercicio abordaremos la creación de un tipo SecuenciaFibonacci. Este tipo debe devolver la

sucesión de Fibonacci: 1, 1, 2, 3, 5,…, hasta un valor concreto que se pasa como parámetro al

constructor. Recuerde que cada valor se calcula sumando los valores de los dos anteriores (v0=1, v1=1,

vn=vn-1 + vn-2, tal que n>1).

o Constructor:

public SecuenciaFibonacci (Long fin)

o Ejemplo de uso:

Iterable<Long> it = new SecuenciaFibonacci(5L); for(Long d: it) {

mostrar(d); }

o Resultado del código de ejemplo:

1

1

2

3

3. Se desea definir un tipo análogo a ArrayBidireccional para listas que se denomine ListaBidireccional.

Implemente la clase ListaBidireccionaImpl teniendo en cuenta que la interfaz ListaBidireccional extiende

de List y que sólo tiene un método nuevo para la propiedad recorrido (análoga a la vista en

ArrayBidireccional) que es consultable y modificable.

o Constructor:

public ListaBidireccionalImpl (Collection<T> l)

o Ejemplo de uso:

List<Integer> laux = new ArrayList<Integer>(); laux.add(2); laux.add(3); ListaBidireccional<Integer> l= new ListaBidireccionalImpl<Integer>(laux); l.setRecorrido(Recorrido.HACIA_ATRAS); for(Integer d: l) {

mostrar(d); }

o Resultado del código de ejemplo:

3

2

Page 16: Fundamentos de Programación - us

16 Fundamentos de Programación

4. Se desea construir un tipo que permita recorrer los caracteres de una cadena de izquierda a derecha y

viceversa. Para ello, se pide implementar un nuevo tipo denominado CadenaIterableBidireccional que

extiende a Iterable y que tiene una propiedad consultable de tipo String, denominada cadena, y una

propiedad tipoRecorrido de tipo Recorrido, consultable y modificable. Tenga en cuenta que el criterio de

igualdad será el de la cadena.

o Constructor:

public CadenaBidireccionalImpl (String s)

o Ejemplo de uso:

CadenaBidireccional l= new CadenaBidireccionalImpl (“HOLA”); l.setRecorrido(Recorrido.HACIA_ATRAS); for(Character d: l) {

mostrar(d); }

o Resultado del código de ejemplo:

A

L

O

H

NOTA: Reutilice ArrayIterable.