Clase de Iteradores en C++ Algoritmos y estructuras de datos II Algoritmos y estructuras de datos II Clase de Iteradores en C++ Hasta ahora vimos: I Clases I Manejo de memoria dinámica I Templates Algoritmos y estructuras de datos II Clase de Iteradores en C++ template < typename T > class ArregloD { T* arreglo ; int espacio ; int ultimo ; public : ArregloD (); ArregloD ( int tamanio ); ArregloD ( const ArregloD <T >& otroArreglo ); ~ ArregloD (); void insertarAtras ( const T & elem ); int tamanio () const ; const T & iesimo ( int i ) const ; T & iesimo ( int i ); }; Algoritmos y estructuras de datos II Clase de Iteradores en C++ Iteradores I ¿Qué es un iterador? I I I Un objeto que permite atravesar un contenedor. En general mantienen una interfaz común y ocultan los detalles de la estructura que iteran. ¿Cómo los usamos en C++? I I En C++ los contenedores iterables contienen una clase interna llamada iterator y métodos para obtener una instancia que itere a partir de algún punto. El estilo de C++ es levemente distinto al estilo que usamos en la materia en los módulos de diseño, en el TP podrán utilizar el que les sea más cómodo. Algoritmos y estructuras de datos II Clase de Iteradores en C++ Declarando un iterador à la Algo 2 template < typename T > class ArregloD { public : ... class Iterador { ArregloD <T >* arreglo ; int indice ; Iterador ( ArregloD <T >* arreglo , int indice ); friend Iterador ArregloD <T >:: crearIt (); // Lo que ? public : bool hayMas () const ; T& actual (); void avanzar (); }; Iterador crearIt (); }; Algoritmos y estructuras de datos II Clase de Iteradores en C++ Definiendo un iterador à la Algo 2 template < typename T > typename ArregloD <T >:: Iterador ArregloD <T >:: crearIt () { Iterador it ( this , 0); return it ; } template < typename T > ArregloD <T >:: Iterador :: Iterador ( ArregloD * a , int i ) : arreglo ( a ) , indice ( i ) {} Algoritmos y estructuras de datos II Clase de Iteradores en C++ Definiendo un iterador à la Algo 2 template < typename T > bool ArregloD <T >:: Iterador :: hayMas () const { return indice < arreglo - > tamanio (); } template < typename T > T & ArregloD <T >:: Iterador :: actual () { return arreglo - > iesimo ( indice ); } template < typename T > void ArregloD <T >:: Iterador :: avanzar () { indice ++; } Algoritmos y estructuras de datos II Clase de Iteradores en C++ Definiendo un iterador à la Algo 2 ¿Qué sucede con el siguiente código? const ArregloD < int >& a = de Al g un aF un c io n (); ArregloD < int >:: Iterador it = a . crearIt (); if ( it . hayMas ()) { int & e = it . actual (); cout << e ; e = 2; } ¿Y si sacamos el const? Algoritmos y estructuras de datos II Clase de Iteradores en C++ Declarando un iterador à la Algo 2 template < typename T > class ArregloD { public : ... class It erador_c onst { const ArregloD <T >* arreglo ; int indice ; Iter ador_con st ( const ArregloD <T >* arreglo , int indice ); friend Itera dor_cons t ArregloD <T >:: crearIt () const ; public : bool hayMas () const ; const T & actual (); void avanzar (); }; Iter ador_con st crearIt () const ; }; Algoritmos y estructuras de datos II Clase de Iteradores en C++ Definiendo un iterador à la Algo 2 template < typename T > typename ArregloD <T >:: Iterad or_const ArregloD <T >:: crearIt () const { Iter ador_con st it ( this , 0); return it ; } template < typename T > ArregloD <T >:: Itera dor_con st :: I terador_ const ( const ArregloD * a , int i ) : arreglo ( a ) , indice ( i ) {} template < typename T > const T & ArregloD <T >:: Itera dor_con st :: actual () { return arreglo - > iesimo ( indice ); } Algoritmos y estructuras de datos II Clase de Iteradores en C++ Ventajas: I Permite manipular eficientemente el contenedor manteniendo oculta su representación interna. I Podemos usar iteradores como “punteros seguros” a la estructura interna sin exponerla. I Permite escribir algoritmos genéricos (asumiendo una interfaz común). Algoritmos y estructuras de datos II Clase de Iteradores en C++ template < typename T > class Lista { struct Nodo { T elem ; Nodo * anterior , * siguiente ; Nodo ( const T & e , Nodo * ant , Nodo * sig ) : elem ( e ) , anterior ( ant ) , siguiente ( sig ) {} }; Nodo * cabeza ; public : class Iterador { Lista <T >* lista ; Nodo * nodo ; Iterador ( Lista * lista , Nodo * nodo ); friend class Lista <T >; public : Iterador insertar ( const T & e ); void remover (); }; Lista (); Iterador agregar ( const T & t ); }; Algoritmos y estructuras de datos II Clase de Iteradores en C++ template < typename T > Lista <T >:: Lista () : cabeza ( NULL ) {} template < typename T > typename Lista <T >:: Iterador Lista <T >:: agregar ( const T & e ) { cabeza = new Nodo (e , NULL , cabeza ); if ( cabeza - > siguiente != NULL ) cabeza - > siguiente - > anterior = cabeza ; typename Lista <T >:: Iterador it ( this , cabeza ); return it ; } Algoritmos y estructuras de datos II Clase de Iteradores en C++ template < typename T > Lista <T >:: Iterador :: Iterador ( Lista <T >* l , Nodo * n ) : lista ( l ) , nodo ( n ) {} template < typename T > typename Lista <T >:: Iterador Lista <T >:: Iterador :: insertar ( const T & e ) { Nodo * nuevo = new Nodo (e , nodo , nodo - > siguiente ); if ( nuevo - > siguiente != NULL ) nuevo - > siguiente - > anterior = nuevo ; nodo - > siguiente = nuevo ; Iterador it ( lista , nuevo ); return it ; } Algoritmos y estructuras de datos II Clase de Iteradores en C++ template < typename T > void Lista <T >:: Iterador :: remover () { if ( nodo == lista - > cabeza ) { lista - > cabeza = nodo - > siguiente ; nodo - > siguiente - > anterior = NULL ; } else { nodo - > anterior - > siguiente = nodo - > siguiente ; if ( nodo - > siguiente != NULL ) nodo - > siguiente - > anterior = nodo - > anterior ; } delete nodo ; } Algoritmos y estructuras de datos II Clase de Iteradores en C++ ¡A la consola! Ejercicio 1. Implementar las siguientes funciones en lista.h template < typename T > class Lista { class Iterador { bool haySiguiente (); T & actual (); void avanzar (); } Iterador crearIt (); } Ejercicio 2. Implementar las siguientes funciones fuera de la clase lista (usando los iteradores ;-) ) int cant ( Lista <T >& l ); // cantidad de elementos de l string to_s ( Lista <T >& l ); // string con el contenido de l Algoritmos y estructuras de datos II Clase de Iteradores en C++ ¡A la consola! Contexto. Se dispone de un registro de personas donde las mismas se anotan informando su nombre y edad. Se desea poder obtener listados de aquellas personas que hayan alcanzando al menos cierta edad. Ejercicio 3. Se cuenta con la implementación del registro en C++, pero no del iterador. Completar la implementación aprovechando el iterador de listas de los módulos básicos. Algoritmos y estructuras de datos II Clase de Iteradores en C++ Recomendaciones al pasar del diseño a la implementación I Por cada módulo hacer una clase y un test. I Hacer una clase interna por cada iterador en el módulo (y una análoga const si tiene sentido). I Recordar que las clases templatizadas no forman unidades de compilación (ojo con los cpp que no deben compilarse). I Tener siempre presente el scope de las variables y los objetos. I Identificar qué cosas deben alocarse en memoria dinámica y qué cosas no. I Hacer delete de todo lo que se hace new, pero de nada más. I LO MAS IMPORTANTE DE TODO ES PROGRAMAR DE FORMA INCREMENTAL. ESCRIBIR UNA FUNCIÓN, SU TEST, COMPILAR Y PROBAR ANTES DE SEGUIR. Algoritmos y estructuras de datos II Clase de Iteradores en C++ ¿Cómo se hace el diseño de esto? Por lo general . . . I Se va a definir un género de módulo por cada iterador I que se explican con secu(α), itUni(α), itMod(α), itBi(α) o similar. I La operación que devuelve el iterador va a describir funcionalmente la secuencia a recorrer, I a pesar de que la implementación no genere dicha lista. I Habrán operaciones para soportar la interfáz de un iterador, I cuyas axiomatizaciones serán casi triviales, I a pesar de que la implementación haga cosas muy complicadas. Algoritmos y estructuras de datos II Clase de Iteradores en C++ Iteradores à la C++ C++, Java, Python y muchos otros lenguajes hace un uso intensivo de iteradores, cada uno tiene su propia manera de hacerlos. En C++: I Los iteradores deben proveer los operadores *, ++, ->, == y otros dependiendo del tipo del iterador. I Los objetos iterables usualmente tiene una clase interna iterator (y/o const iterator) de forma similar a como lo hicimos hasta ahora. I Los objetos iterables usualmente aceptan los métodos begin() y end() (y potencialmente rbegin() y rend()). Algoritmos y estructuras de datos II Clase de Iteradores en C++ template < typename T > class Lista { Nodo * cabeza ; Nodo * fin ; ... public : class iterator { Lista <T >* lista ; Nodo * nodo ; bool valido ; ... public : T & operator *(); bool operator ==( const Lista <T >:: iterator & otro ) const ; bool operator !=( const Lista <T >:: iterator & otro ) const ; iterator & operator ++(); iterator operator ++( int ); iterator & operator - -(); iterator operator - -( int ); }; iterator begin (); iterator end (); }; Algoritmos y estructuras de datos II Clase de Iteradores en C++ template < typename T > T & Lista <T >:: iterator :: operator *() { return nodo - > elem ; } template < typename T > typename Lista <T >:: iterator & Lista <T >:: iterator :: operator ++() { valido = nodo - > siguiente != NULL ; if ( valido ) nodo = nodo - > siguiente ; return * this ; } template < typename T > typename Lista <T >:: iterator Lista <T >:: iterator :: operator - -( int ) { iterator it (* this ); valido = nodo - > anterior != NULL ; if ( valido ) nodo = nodo - > anterior ; return it ; } template < typename T > bool Lista <T >:: iterator :: operator ==( const Lista <T >:: iterator & otro ) const { return valido == otro . valido && nodo == otro . nodo ; } Algoritmos y estructuras de datos II Clase de Iteradores en C++ Algoritmos genéricos sobre iteradores en C++ El header algorithm tiene definidas un gran número de funciones con clases templatizadas asumiendo que cumplen con la interfaz de los iteradores. Por lo que cualquier clase que que siga correctamente el patrón automáticamente tiene todos esos algoritmos a su disposición sin necesidad de implementarlos. Ejemplos: I find I count I replace I reverse I sort I random shuffle I min I max Algoritmos y estructuras de datos II Clase de Iteradores en C++ Algoritmo de búsqueda de algorithm.h template < class InputIterator , class T > InputIterator find ( InputIterator first , InputIterator last , const T & value ) { for (; first != last && * first != value ; ++ first ); return first ; } Algoritmos y estructuras de datos II Clase de Iteradores en C++ Tipos de iteradores Los iteradores en C++ se separan en distintas categorı́as, lo que permite implementar algoritmos genéricos de distinta forma para cada tipo de iterador: 1. Input 2. Output 3. Forward 4. Bidirectional 5. Random access Para definir a qué categorı́a pertenece un iterador se utilizan miembros de la clase iterator traits. Esto puede hacerse de distintas formas, pero usualmente incluye herencia o especialización de templates por lo que queda fuera de este curso. Algoritmos y estructuras de datos II Clase de Iteradores en C++ Ejercicio Implemente una clase Rosetree (un árbol con una cantidad arbitraria de hijos) con un iterador preorder à la C++. Permita agregar y eliminar elementos a través del iterador retornando un iterador nuevo cuando corresponda. Puede hacer uso de las listas enlazadas y los algoritmos sobre iteradores de la librerı́a standard: # include < list > # include < algorithm > En la página http://www.cplusplus.com/reference/ pueden encontrar la documentación de estos módulos. Algoritmos y estructuras de datos II Clase de Iteradores en C++