Foros del Web » Programación para mayores de 30 ;) » C/C++ »

[SOLUCIONADO] Incrementar un iterador de un vector<MyClass>

Estas en el tema de Incrementar un iterador de un vector<MyClass> en el foro de C/C++ en Foros del Web. Hola amgos, les traigo una cuestión interesantes sobre punteros. Primero tengo entendido que un iterador es un puntero de un contenedor por ejemplo: @import url("http://static.forosdelweb.com/clientscript/vbulletin_css/geshi.css"); ...
  #1 (permalink)  
Antiguo 02/02/2016, 18:21
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 9 años, 10 meses
Puntos: 1
Incrementar un iterador de un vector<MyClass>

Hola amgos, les traigo una cuestión interesantes sobre punteros. Primero tengo entendido que un iterador es un puntero de un contenedor por ejemplo:
Código C++:
Ver original
  1. vector<in>::iterator it;
y por tanto se puede hacer un for sobre él:
Código C++:
Ver original
  1. for (it = v.begin(); it != v.end(); it++){
  2.         //algo bonito;
  3.     }
Sin embargo, cree una función que me retorna un iterador, lo que hace es buscar en un vector de una clase por ejemplo, el elemento que sea igual a un número y me retorna su posición como iterador:
Código C++:
Ver original
  1. ector<MyClass>::iterator donde(vector<MyClass>& v, int i){
  2.     vector<MyClass>::iterator it;
  3.     for (it = v.begin(); it != v.end(); it++){
  4.         if (it->getId() == i) return it;
  5.     }
  6.     return it;
  7. }
y luego en el main traté de incrementar el iterador con un simple it++ pero no me funciona.

Código C++:
Ver original
  1. int main(){
  2.     vector<MyClass> v;
  3.     v.push_back(MyClass(2));
  4.     v.push_back(MyClass(8));
  5.     v.push_back(MyClass(10));
  6.    
  7.     vector<MyClass>::iterator it = donde(v, i);
  8.     it++;
  9.     it->setId(-8);
  10.  
  11.     cin.sync();
  12.     cin.get();
  13.     return 0;
  14. }

Todo funciona bien excepto el incremento, no sé bien cual es el problema pues según la aritmética de punteros si que puedo hacer eso.

saludos
  #2 (permalink)  
Antiguo 02/02/2016, 18:27
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 9 años, 10 meses
Puntos: 1
Respuesta: Incrementar un iterador de un vector<MyClass>

Vale era una cuestión muy simple, incrementaba cuando ya había llegado al final al v.end().
  #3 (permalink)  
Antiguo 03/02/2016, 01:37
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 7 meses
Puntos: 204
Respuesta: Incrementar un iterador de un vector<MyClass>

Código C++:
Ver original
  1. vector<MyClass>::iterator donde(vector<MyClass>& v, int i){
  2.     vector<MyClass>::iterator it;
  3.     for (it = v.begin(); it != v.end(); it++){
  4.         if (it->getId() == i) return it;
  5.     }
  6.     return it;
  7. }

Un par de consideraciones sobre esa función:
  • Poner dos return en una función tan sencilla es algo a evitar. El índice de mantenimiento del código suele empeorar si una función tiene más de un return. La excepción a esa regla se suele dar si la función es larga y los return se deben a chequeos previos a ejecutar el código de la función.
  • Existe la función find_if que te permite eliminar el for... si bien escribes un poquito más de código éste es menos propenso a errores (por ejemplo que se borre el '!' de la condicional del for.

Una primera aproximación podría ser:

Código C++:
Ver original
  1. vector<MyClass>::iterator donde(vector<MyClass>& v, int i){
  2.     return  std::find_if(v.begin(),v.end(),
  3.                          [i](const MyClass& item)
  4.                          { return item.getId() == i; });
  5. }

Y ahora, con respecto a tu main, ignorando el hecho de que i no está ni declarada pero asumo que en tu código su uso es correcto, ...

Código C++:
Ver original
  1. int main(){
  2.     vector<MyClass> v;
  3.     v.push_back(MyClass(2));
  4.     v.push_back(MyClass(8));
  5.     v.push_back(MyClass(10));
  6.        
  7.     vector<MyClass>::iterator it = donde(v, i);
  8.     it++;
  9.     it->setId(-8);
  10.      
  11.     cin.sync();
  12.     cin.get();
  13.     return 0;
  14. }

C++11 ya incluye los incializadores de listas, lo que simplifica el trabajo de rellenar vectores con datos fijos:

Código C++:
Ver original
  1. int main(){
  2.     // opción 1
  3.     vector<MyClass> v { MyClass(2), MyClass(8), MyClass(10) };
  4.  
  5.     // opción 2
  6.     // esta opción funcionará siempre que el constructor
  7.     // MyClass::MyClass(int) no esté marcado como explicit
  8.     vector<MyClass> v { 2, 8, 10 };
  9.          
  10.     vector<MyClass>::iterator it = donde(v, i);
  11.     it++;
  12.     it->setId(-8);
  13.      
  14.     cin.sync();
  15.     cin.get();
  16.     return 0;
  17. }

Un saludo.
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #4 (permalink)  
Antiguo 08/02/2016, 17:14
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 9 años, 10 meses
Puntos: 1
Respuesta: Incrementar un iterador de un vector<MyClass>

Eferion siempre presente y activo en el foro, me alegro mucho de contar contigo. Es impresionante la cantidad de cosas que se pueden aprender buscando otras jeje (yo que pensaba que ya medio dominaba lo básico de c++ ).

Cita:
Iniciado por eferion Ver Mensaje
[HIGHLIGHT="C++"]vector<MyClass>::iterator [*]Poner dos return en una función tan sencilla es algo a evitar. El índice de mantenimiento del código suele empeorar si una función tiene más de un return. La excepción a esa regla se suele dar si la función es larga y los return se deben a chequeos previos a ejecutar el código de la función.
Que es el mantenimiento de código? he buscado por google pero no encuentro nada especifico, supongo que tiene que ver con la métrica del código.

Cita:
Iniciado por eferion Ver Mensaje
[*]Existe la función find_if que te permite eliminar el for... si bien escribes un poquito más de código éste es menos propenso a errores (por ejemplo que se borre el '!' de la condicional del for.[/LIST]
Una primera aproximación podría ser:

Código C++:
Ver original
  1. vector<MyClass>::iterator donde(vector<MyClass>& v, int i){
  2.     return  std::find_if(v.begin(),v.end(),
  3.                          [i](const MyClass& item)
  4.                          { return item.getId() == i; });
  5. }
Super, no sabía que existía este tipo de funciones. en el find_if que lleva 3 argumentos, los dos primeros son iteradores y el tercero según cplusplus es un UnaryPredicate (función que transforma el contendido de un elemento a booleano según entiendo) y tu pones un [I](const MyClass& item), lo que no entiendo es que significa ese "[i]" y entre paréntesis una clase que pasas por referencia. Los [] son operadores para entrar en ciertos contenedores tipo "v[3]" y luego con los paréntesis ya me volví loco jajaja, pues creí que solo servían para llamar a funciones o para separar operaciones matemáticas largas.

Cita:
Iniciado por eferion Ver Mensaje

C++11 ya incluye los incializadores de listas, lo que simplifica el trabajo de rellenar vectores con datos fijos:

Código C++:
Ver original
  1. int main(){
  2.     // opción 1
  3.     vector<MyClass> v { MyClass(2), MyClass(8), MyClass(10) };
  4.  
  5.     // opción 2
  6.     // esta opción funcionará siempre que el constructor
  7.     // MyClass::MyClass(int) no esté marcado como explicit
  8.     vector<MyClass> v { 2, 8, 10 };
  9.          
  10.     vector<MyClass>::iterator it = donde(v, i);
  11.     it++;
  12.     it->setId(-8);
  13.      
  14.     cin.sync();
  15.     cin.get();
  16.     return 0;
  17. }
Hay algunos ejemplos en internet que mencionan más o menos los diferentes usos de explicit, podrías explicarme un poco más qué es y cuando se puede usar?

Muchas gracias de nuevo por tu tiempo y paciencia. Un saludo desde Colombia.
  #5 (permalink)  
Antiguo 09/02/2016, 02:30
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 7 meses
Puntos: 204
Respuesta: Incrementar un iterador de un vector<MyClass>

Cita:
Iniciado por dmorill Ver Mensaje
Que es el mantenimiento de código? he buscado por google pero no encuentro nada especifico, supongo que tiene que ver con la métrica del código.
Después de realizar una aplicación toca el mantenimiento. Lo lógico es que las aplicaciones no estén exentas de fallos que toca corregir después del lanzamiento de la aplicación.

Además lo más normal es que una vez lanzada la primera versión se introduzcan nuevas funcionalidades y mejoras...

Uno de los factores que afecta de forma directa al coste que supone realizar estas tareas es el diseño de la aplicación. Un buen diseño reduce el coste de mantener el software en funcionamiento mientras que un mal diseño tiene el efecto contrario.

¿Cómo mejorar el índice de manteminiento? Suele haber varias directrices y de hecho verás que algunas son incluso antagónicas (lo que implica encontrar un punto de equilibrio):

  • Reducir el número de líneas de cada función: Una función con 20 líneas suele ser más sencilla de revisar que una de 10.000 (y créeme que las hay)
  • Reducir la profundidad de la herencia: La herencia suele crear acoplamiento (te obliga a arrastrar cosas que no necesitas)... éste es malo porque un cambio en tu código puede afectar a más áreas de la aplicación de las que piensas... mayor acoplamiento = más test a realizar.
  • Reducir la cantidad de código: Hasta cierto punto suena lógico... cuantas menos líneas de código tenga una aplicación menos puntos de error va a tener.
  • Tabular correctamente: Un código bien tabulado facilita enormemente su lectura lo cual supone una gran ventaja a la hora de lidiar con errores.
  • Usar nombres coherentes: Si te encuentras una variable llamada `dd` ¿Cual es su utilidad? vete tu a saber. ¿Y una función llamada dd()? En línea con el punto anterior, usar nombres coherentes ayuda a entender el código lo cual te permite ahorrar minutos o incluso horas intentando descifrar el código.
  • Evitar en la medida de lo posible el uso de "wildcards". Vale que todos podemos llegar a tener una obsesión enfermiza con el rendimiento, pero hay que saber ver que no es necesario optimizar al extremo todas y cada una de las funcionalidades de una aplicación. Hay algoritmos super optimizados para, por ejemplo este que tienen grandes dosis de ingeniería. Queda muy chulo encontrarlo y pensar "lo pongo en mi aplicación que esto mola"... después te encuentras con que tu programa no funciona como esperas y... ¿dónde está el error? Si se ponen cosas así es mejor dejarlas muy bien documentadas porque suele ser arriesgado tocar cualquier punto de ese código. Además, ¿tiene sentido que una acción provocada por el usuario tarde 1us en vez de 1ms? como norma general no si con ello se sacrifica la legibilidad del codigo de sobremanera.
  • ... Podría poner puntos adicionales durante toda la mañana pero creo que con los expuestos queda clara mi postura :)

Cita:
Iniciado por dmorill Ver Mensaje
Super, no sabía que existía este tipo de funciones. en el find_if que lleva 3 argumentos, los dos primeros son iteradores y el tercero según cplusplus es un UnaryPredicate (función que transforma el contendido de un elemento a booleano según entiendo) y tu pones un [I](const MyClass& item), lo que no entiendo es que significa ese "[i]" y entre paréntesis una clase que pasas por referencia. Los [] son operadores para entrar en ciertos contenedores tipo "v[3]" y luego con los paréntesis ya me volví loco jajaja, pues creí que solo servían para llamar a funciones o para separar operaciones matemáticas largas.
Te presento un elemento que pasó a formar parte de C++ con la entrada en vigor del estándar C++11, las lambdas. Una lambda es una función creada al momento. Su uso es exactamente el mismo que el de cualquier función que podamos declarar de la forma tradicional. ¿Por qué usar entonces una lambda? A mi me gustan porque puedo poner el código en el mismo sitio donde voy a usarlo. ¿Y por qué no opto por usar entonces un bucle y me ahorro la lambda? Pues porque las funciones tipo std::find_if construyen el bucle por mí, lo que me permite centrarme en indicar qué es lo que quiero conseguir.

Como nota adicional yo generalmente uso las lambdas en chequeos sencillos de una o dos líneas para no complicar el código.

Un Ejemplo con una función al estilo tradicional y una lambda capaz de sustituirla:

Código C++:
Ver original
  1. bool func(int valor)
  2. { return valor==2; }
  3.  
  4. int main()
  5. {
  6.   std::vector<int> valores{1,2,3,4,5,6,7,8};
  7.  
  8.   // Sin lambda
  9.   auto it = std::find_if(valores.begin(),valores.end(),func);
  10.  
  11.   // Con lambda
  12.   auto it = std::find_if(valores.begin(),valores.end(),
  13.                                    [](int valor){ return valor == 2; });
  14.  
  15.   std::cout << *it;
  16. }

En cuanto a tu pregunta sobre el formato de las lambdas te comento. La estructura general de una lambda es [1](2) -> 3 {4}, donde:
  1. Entre los corchetes se indican las variables que queremos usar dentro de la lambda. Estas variables se pueden pasar como referencia, lo que permite a la lambda modificar su valor. Si los corchetes están vacios entonces la lambda no puede acceder a ninguna variable de nuestra función. Esta sección admite varios usos:
    • [] La lambda no tiene acceso a variables externas.
    • [&] La lambda tiene acceso a todas las variables declaradas en la función y además cualquier cambio en dichas variables se reflejará fuera de la lambda
    • [=] Una copia de todas las variables externas está disponible dentro de la lambda.
    • [=, &var] Se copian todas las variables menos var que se pasa por referencia.
    • [var] Únicamente estará disponible una copia de la variable var
    • [var,&var2] Disponemos de una copia de var y var2 es accesible por referencia.
    • [this] Se copia el puntero de la clase actual.
  2. Los paréntesis delimitan los argumentos que va a recibir la lambda... como en el caso de cualquier otra función.
  3. Este punto puede ser opcional y permite indicar el valor de retorno de la lambda. En funciones de una sola línea el compilador es capaz de identificar este valor por lo que podemos ignorar este punto.
  4. El código de la lambda... como si fuese una función más

El puntero a una lambda se puede almacenar en una variable... para ello lo ideal es usar auto o std::function:

Código C++:
Ver original
  1. #include <iostream>
  2. #include <functional>
  3.  
  4. int main()
  5. {
  6.   // Las dos definiciones son equivalentes porque el compilador es capaz de deducir el tipo de retorno de la lambda
  7.   auto func = [](int valor) {std::cout << valor << std::endl; };
  8.   auto func = [](int valor) -> void {std::cout << valor << std::endl; };
  9.  
  10.   func(3);
  11.  
  12.   // también se puede almacenar en std::function
  13.   std::function<void(int)> func2 = func;
  14.  
  15.   func2(5);
  16. }

Cita:
Iniciado por dmorill Ver Mensaje
Hay algunos ejemplos en internet que mencionan más o menos los diferentes usos de explicit, podrías explicarme un poco más qué es y cuando se puede usar?
explicit es un modificador que se pone en los constructores (y en los operadores de conversión para C++11 en adelante). Al añadir esta palabra a un constructor evitamos que se realicen conversiones implícitas. Por ejemplo:

Código C++:
Ver original
  1. struct Foo
  2. {
  3.   Foo(int v)
  4.     : valor(v)
  5.   { }
  6.  
  7.  
  8.   int valor;
  9. };
  10.  
  11. struct Foo2
  12. {
  13.   explicit Foo2(int v)
  14.     : valor(v)
  15.   { }
  16.  
  17.  
  18.   int valor;
  19. };
  20.  
  21. void func(Foo foo)
  22. { std::cout << foo.valor << std::endl; }
  23.  
  24. void func2(Foo2 foo)
  25. { std::cout << foo.valor << std::endl; }
  26.  
  27. int main()
  28. {
  29.   // Compila. Se llama de forma implícita a Foo(int)
  30.   func(1);
  31.  
  32.   // No compila. La llamada a Foo2(int) ha de indicarse de forma explícita.
  33.   func2(1);
  34.  
  35.   // Compila
  36.   func2(Foo2(1));
  37. }

Esta palabra evita que se realicen determinadas operaciones de forma colateral. Nos obliga a indicar de forma explícita las conversiones lo que hace que seamos un poco más conscientes de qué es lo que realmente se está ejecutando.

Un saludo.
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #6 (permalink)  
Antiguo 09/02/2016, 08:00
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 9 años, 10 meses
Puntos: 1
Respuesta: Incrementar un iterador de un vector<MyClass>

Hola eferion muchas gracias por tu tiempo estoy aprendiendo mucho de tus comentarios.

Las funciones lambda y el mantenimiento me ha quedado muy claro. Son cosas que no sabía y iré incluyéndolo en mis programas.

Cita:
Iniciado por eferion Ver Mensaje
explicit es un modificador que se pone en los constructores (y en los operadores de conversión para C++11 en adelante). Al añadir esta palabra a un constructor evitamos que se realicen conversiones implícitas. Por ejemplo:

Código C++:
Ver original
  1. struct Foo{
  2.   Foo(int v): valor(v) { }
  3.  
  4.   int valor;
  5. };
  6.  
  7. struct Foo2{
  8.   explicit Foo2(int v): valor(v)  { }
  9.  
  10.   int valor;
  11. };
  12.  
  13. void func(Foo foo){
  14.  std::cout << foo.valor << std::endl;
  15. }
  16.  
  17. void func2(Foo2 foo){
  18.  std::cout << foo.valor << std::endl;
  19. }
  20.  
  21. int main(){
  22.   // Compila. Se llama de forma implícita a Foo(int)
  23.   func(1);
  24.  
  25.   // No compila. La llamada a Foo2(int) ha de indicarse de forma explícita.
  26.   func2(1);
  27.  
  28.   // Compila
  29.   func2(Foo2(1));
  30. }

Esta palabra evita que se realicen determinadas operaciones de forma colateral. Nos obliga a indicar de forma explícita las conversiones lo que hace que seamos un poco más conscientes de qué es lo que realmente se está ejecutando.

Un saludo.
Los explicit no los entendí del todo, en el ejemplo que me diste (modifiqué los espacios a los que yo estoy más acostumbrado jeje) en las dos estructuras definiste un constructor idéntico salvo por el explicit (llamando a una función valor(v), que no las has definido pero al tener el nombre de la atributo de la estructura supongo que es una asignación), y al usarlo también se hace de forma idéntica, donde está lo diferente? solo es a nivel de compilación?

Una pregunta aprovechando que estamos hablando de iteradores, cree una función cuya declaración es:
Código C++:
Ver original
  1. vector<int>::iterator next(vector<int>& v, vector<int>::iterator it);

Y lo que hace es que si se llama por primera vez retorna v.begin(); y sino retorna it +1 (intenté usar it++ pero me retornaba el anterior sin sumarle uno). El problema está en como identificar ese "por primera vez"para lo cual lo que pensé fue, cuando creo un iterador que debo pasarlo como argumento la primera vez puedo hacer que éste sea un iterador uno así:

Código C++:
Ver original
  1. vector<int>::iterator it2 = 0;  // o NULL

Y dentro de la función next me pregunto si ese iterador es nulo, en tal caso retorna v.begin(); sino pues le suma al iterador y lo devuelve. La cosa es que no se puede hacer esa asignación del 0 o NULL al iterador de un vector. Al final me toco crear otro vector y tomarlo como referencia y no usarlo sino para apuntar a él al principio. Cómo lo habrías hecho?

saludos,
  #7 (permalink)  
Antiguo 09/02/2016, 08:32
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 7 meses
Puntos: 204
Respuesta: Incrementar un iterador de un vector<MyClass>

Cita:
Iniciado por dmorill Ver Mensaje
Los explicit no los entendí del todo, en el ejemplo que me diste (modifiqué los espacios a los que yo estoy más acostumbrado jeje) en las dos estructuras definiste un constructor idéntico salvo por el explicit (llamando a una función valor(v), que no las has definido pero al tener el nombre de la atributo de la estructura supongo que es una asignación)
Un constructor en C++ tiene la siguiente forma:

Código C++:
Ver original
  1. POO::POO([argumentos])
  2.   : [inicializadores]
  3. {
  4.   [mas_codigo]
  5. }

Lo que tu presupones que es una función es una simple inicialización, estoy asignando a la variable miembro valor el dato que se le ha pasado como parámetro al constructor.

Tratándose de C++11 también podría haberlo declarado así:

Código C++:
Ver original
  1. struct Foo{
  2.   Foo(int v): valor{v} { }
  3.  
  4.   int valor;
  5. };

Así habría quedado aún más claro que no estoy llamando a una función. No lo he hecho porque la gente aún está demasiado verde en cuanto a los nuevos estándares de C++ se refiere (y eso que de C++11 ya han pasado 5 años).

Cita:
Iniciado por dmorill Ver Mensaje
y al usarlo también se hace de forma idéntica, donde está lo diferente? solo es a nivel de compilación?
Exacto, es una de esas palabras clave que hacen que el código tenga que seguir una serie de reglas determinadas para compilar. ¿Por qué usarlo? Porque puede ayudar a detectar errores de código:

Código C++:
Ver original
  1. struct Punto
  2. {
  3.   int x;
  4.   int y;
  5.  
  6.   Punto(int x=0, int y=0)
  7.     : x(x), y(y) // Inicializacion de las variables miembro
  8.   { }
  9. };
  10.  
  11. int main()
  12. {
  13.   Punto p = 1;
  14.  
  15.   std::cout << "(" << p.x << "," << p.y << ")" << std::endl;
  16. }

Este código es, tal cual, compilable y perfectamente funcional. Una estructura para almacenar puntos 2D con un constructor. ¿Crees que es conveniente el uso que se le da en el main? Yo personalmente no porque si me fijo en la línea

Código C++:
Ver original
  1. Punto p = 1;

No tengo ni la más remota idea de qué hace... ¿Es Punto un typedef de int? ¿Si Punto permite almacenar puntos 2D... qué coordenada estoy guardando? ¿Se está llamando a un constructor o a un operador de asignación que hace conversiones de algún tipo?

Desde luego el código no es nada claro. Sin embargo con un explicit:

Código C++:
Ver original
  1. struct Punto
  2. {
  3.   int x;
  4.   int y;
  5.  
  6.   explicit Punto(int x=0, int y=0)
  7.     : x(x), y(y) // Inicializacion de las variables miembro
  8.   { }
  9. };
  10.  
  11. int main()
  12. {
  13.   Punto p = 1; // Esto ya no compila
  14.  
  15.   // Pero esto sí. Aquí ya tenemos que llamar explícitamente al constructor,
  16.   // por lo que aunque hagamos una llamada poco clara al menos nos obliga
  17.   // a ser conscientes de cómo estamos creando el objeto... En este caso un
  18.   // rápido repaso al constructor nos indica que, si no se indica el valor de y,
  19.   // éste por defecto será 0.
  20.   Punto p = Punto(1);
  21.  
  22.   std::cout << "(" << p.x << "," << p.y << ")" << std::endl;
  23. }

Cita:
Iniciado por dmorill Ver Mensaje
Una pregunta aprovechando que estamos hablando de iteradores, cree una función ...

Cómo lo habrías hecho?
Con lo que expones no veo motivo para que esa función exista. Si necesitas iterar sobre un vector no hay necesidad de llamar a una función. Tendrás que poner un caso de uso más concreto que explique la necesidad de usar esa función... y algo de código también sería de agradecer.

En cuanto a tu problema con it++ comentarte que lo mismo deberías echar un vistazo al tema de pre-incrementos y post-incrementos:

Código C++:
Ver original
  1. int i = 0;
  2. int j = i++; // post-incremento i=1, j=0
  3. int k = ++i; // pre-incremento i=2, k=2

Un saludo.
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #8 (permalink)  
Antiguo 11/02/2016, 08:55
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 9 años, 10 meses
Puntos: 1
Respuesta: Incrementar un iterador de un vector<MyClass>

Hola eferion, no quiero ser pesado y sé que te agradezco en cada respuesta pero creo que es necesario. Gracias por responder ya que puedo seguir aprendiendo de tu conocimiento .

Cita:
Iniciado por eferion Ver Mensaje
Código C++:
Ver original
  1. struct Punto{
  2.   int x;
  3.   int y;
  4.  
  5.   Punto(int x=0, int y=0) : x(x), y(y){ }
  6. };
  7.  
  8. int main(){
  9.   Punto p = 1;
  10.  
  11.   std::cout << "(" << p.x << "," << p.y << ")" << std::endl;
  12. }
[
Me he quedado de piedra, no debería compilar pues según entiendo el "="no está definido para esas estructuras. (creo que a eso se le llama sobrecarga, definir un uso al operador "="). Tuve que compilarlo yo mismo y si de hecho funciona, que peligro usarlo así de ese modo, por qué permite c++ eso? Cambie el struct por un class y ya no compila, es decir que solo es para estructuras.

Cita:
Iniciado por eferion Ver Mensaje
Con lo que expones no veo motivo para que esa función exista. Si necesitas iterar sobre un vector no hay necesidad de llamar a una función. Tendrás que poner un caso de uso más concreto que explique la necesidad de usar esa función... y algo de código también sería de agradecer.
[
El código es super largo pero pondré lo respecto a la pregunta, si quieres el resto solo de dices y con gusto lo pongo todo. Te describo lo que estaba haciendo.

Imagina que tengo los nodos a. de la siguiente gráfica:


Sé que la siguiente explicación es larga y eso que no pongo todo jeje espero no aburrirte:

Y quiero encontrar todos los posibles ciclos, para ello hay que hacer de dos algoritmos, el primero (kosajaru algorithm) nos permite encontrar los componentes fuertemente conexas, es decir aquellos nodos en los que se pueden ir de uno a otro. en el ejemplo en b. nos muestra el el algoritmo encontró 3 componente fuertemente conexas (CFC). El segundo (johnson algorihtm) permite encontrar todos los ciclos de una CFC.
luego de que el algoritmo de johson encuentra los ciclos de una componente, borra un nodo y pide calcular de nuevo las CFC de ese nuevo grafo sin un nodo.

En el algoritmo de johnson lo que hace es una especie de arbol para recorrer todos los nodos y de cada uno de ellos sus nodos adyacentes. primero se selecciona por ejemplo el primer vecino hasta que se revisa si es o no ciclo luego de regresa al nodo anterior y se va por el siguiente vecino.

Código C++:
Ver original
  1. include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6.  
  7. class nodo{
  8. private:
  9.     int _id;
  10.     vector<int> _vVecinos;   //vector con los vecinos adyacentes del nodo
  11.     // por ejemplo nodo 3 tiene vecinos 2,4 y 6
  12.     // otros atributos
  13. public:
  14.     nodo(int id, vector<int>& v) { _id = id; _vVecinos = v; }   //constructor para el ejemplo
  15.     //otras funciones miembro
  16. };
  17.  
  18. vector<vector<nodo>> kosajaru(vector<nodo>& vNodos);   
  19. //enctrega un vector donde cada componente es un                                           
  20. //conjunto de nodos según nuestro ejemplo al principo es así                                           
  21. // 0 {1, 2, 3, 4, 5, 6}                                        
  22. // 1 {8, 9}                                        
  23. // 2 {7}
  24.  
  25. vector<int>::iterator nextVecino(vector<int>::iterator itAct, vector<int>& vVecino, vector<int>::iterator itNULL){
  26.     if (*itAct == *itNULL){   // para saber si es la primera vez que pido vecino.
  27.         return vVecino.begin();
  28.     }else{
  29.         return itAct + 1;   //se supone que esto es itAct++ pero no me funciona así jeje.
  30.     }
  31. }
  32.  
  33. int main(){
  34.     //lectura de datos de todos los nodos, pongo uno como ejemplo
  35.     vector<int> v{ 2, 4, 6};    //estos son int pero podrían ser nodos
  36.     nodo n(3, v);
  37.     vector<nodo> vNodos{nodo(3,v)}; //este debe tener todos los nodos
  38.     //
  39.     vector<vector<nodo>> CFC = kosajaru(vNodos);
  40.  
  41.     while (CFC.empty() == false){   //cada vez el CFC queda con menos componentes hasta que queda vacío
  42.         // aqui va el algoritimo de johson y es aqui donde nicesito lo del iterador que hice como te comenté
  43.         // necesito recorrer cada componente la priemra tiene los nodos 1,2,3,4,5,6 y cada nodo tiene su
  44.         // vector de vecinos que debo recorrer pero guardando en un vector de stack el ciclo que voy probando
  45.         // y también debo recordar en que vecino voy para el algoritmo así que cree un vector
  46.         //  punteros donde la pocición del vector es el vecino del nodo del stack.
  47.         // Todo es mejor con un ejemplo por ejemplo primero defino mi origen en 1 y lo pongo en el stack
  48.         // , luego veo sus vecinos, seleccionó uno y lo pongo en en mi stack y ahora veo los vecínos de éste
  49.         // y así, pero cuando regrese debo recordar qué vecino iba para seleccionar el siguiente. Aquí esta
  50.         // dicha función next de la que te hable.
  51.         //stack ejemplo: {1,2,3,6,4,5} esto es un posible ciclo que estoy revisando
  52.         //vector de iteradores ejemplpo: {2*,3*,6*,4*,5*,1*} donde por ejemplo 2* es la dirección del vecino
  53.         // del vector de vecinos del nodo 1.
  54.         vector<vector<int>::iterator> vit;
  55.         vector<int> stack();
  56.         //entonces cuando regreso yo solo le pregunto en que vecino va y que me de el siguiente, con la función nextVecino.
  57.        
  58.         //resto del codigo jeje
  59.     }
  60.  
  61.     cin.get();
  62. }

No sé si quedo más claro jeje. Si quieres más información con gusto te la pongo, pero no quiero extender tanto el tema para que sea más fácil de leer pues valoro tu tiempo.

saludos,
  #9 (permalink)  
Antiguo 11/02/2016, 09:33
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 7 meses
Puntos: 204
Respuesta: Incrementar un iterador de un vector<MyClass>

Cita:
Iniciado por dmorill Ver Mensaje
Me he quedado de piedra, no debería compilar pues según entiendo el "="no está definido para esas estructuras.
Código C++:
Ver original
  1. struct Punto{
  2.   int x;
  3.   int y;
  4.  
  5.   Punto(int x=0, int y=0)
  6.     : x(x), // estoy copiando un int en otro int
  7.       y(y) // estoy copiando un int en otro int
  8.   { }
  9.  
  10.   // Equivalencia más conocida:
  11.   Punto(int x=0, int y=0)
  12.   {
  13.     this->x = x;
  14.     this->y = y;
  15.   }
  16. };

Pregunta: ¿Por qué no hace falta usar (y de hecho no puedes) this en el primer constructor?

Porque la sección que sigue a los dos puntos es única y exclusivamente para inicializar la clase, luego no permite modificar elementos que no pertenezcan a la misma. Entonces, el primer x que sigue a los dos puntos únicamente puede ser la variable miembro... en el caso de la x que está entre los paréntesis, ésta podría ser bien la variable miembro o bien el parámetro de la función. Como en caso de colisión de nombres el compilador va a usar el que tenga un ámbito menor en este caso va a elegir SIEMPRE la variable pasada como parámetro (el ámbito de la variable miembro es mayor porque al salir del constructor la variable miembro sigue existiendo).

Pregunta: ¿Qué aporta usar esta forma a la hora de diseñar los constructores?

Las ventajas son varias:
  • Es la única forma de llamar a los constructores de las clase padre en el caso de herencia.
  • A partir del estándar C++11 se pueden llamar a constructores de la misma clase desde cualquier otro constructor... estas llamadas únicamente se pueden hacer con el diseño que te he propuesto.
  • Crear constructores de la segunda forma puede provocar una pérdida de rendimiento. Si alguna variable miembro es una clase y no se inicializa en la zona de inicialización el constructor llamará a su constructor por defecto... si después se modifica el valor de dicha variable usando el operador = se realizará una copia... dos llamadas que normalmente se superponen en vez de una:

    Código C++:
    Ver original
    1. struct A
    2. {
    3.   A()
    4.   { std::cout << "A::A()" << std::endl; }
    5.  
    6.   A(int)
    7.   { std::cout << "A::A(int)" << std::endl; }
    8.  
    9.   A(const A&)
    10.   { std::cout << "A::A(const A&)" << std::endl; }
    11.  
    12.   A& operator=(const A&)
    13.   {
    14.     std::cout << "A& operator=(const A&)" << std::endl;
    15.     return *this;
    16.   }
    17. };
    18.  
    19. struct B
    20. {
    21.   A a;
    22.  
    23.   B(A& copia)
    24.   {
    25.     a = copia;
    26.   }
    27.  
    28.   // El bool de este constructor lo he puesto únicamente para diferenciar ambos constructores
    29.   B(A& copia, bool)
    30.     : a(copia)
    31.   { }
    32. };
    33.  
    34. int main()
    35. {
    36.   A dummy;
    37.  
    38.   std::cout << std::endl
    39.             << "Dos llamadas: constructor por defecto y operador de asignación."
    40.             << std::endl;
    41.   B b1(dummy);
    42.  
    43.   std::cout << std::endl
    44.             << "Una única llamada: constructor copia"
    45.             << std::endl;
    46.   B b2(dummy,false);
    47. }

Cita:
Iniciado por dmorill Ver Mensaje
Tuve que compilarlo yo mismo y si de hecho funciona, que peligro usarlo así de ese modo, por qué permite c++ eso?
Con lo expuesto anteriormente queda claro que no es peligroso. Piensa que C++ implementa por defecto el operador de asignación y realiza una copia simple y llana del estado del objeto... esta opción está bien mientras no tires de memoria dinámica, en cuyo caso tienes que reimplementar dicho operador o directamente evitar su implementación. Para esto último puedes marcar la función como borrada y así evitas que el compilador cree la versión por defecto (nuevamente C++11):

Código C++:
Ver original
  1. struct Test1
  2. {
  3.   Test1& operator=(const Test1&) = delete;
  4. };
  5.  
  6. struct Test2
  7. {
  8. };
  9.  
  10. int main()
  11. {
  12.   Test1 t11, t12;
  13.  
  14.   t11 = t12; // Error, operador de asignación no disponible
  15.  
  16.   Test2 t21,t22;
  17.   t21 = t22; // OK
  18. }

Esto es aplicable también a constructores, funciones heredadas, ...


Cita:
Iniciado por dmorill Ver Mensaje
Cambie el struct por un class y ya no compila, es decir que solo es para estructuras.
Lógico. La única diferencia en C++ entre un struct y una class es la visibilidad por defecto. En un struct los elementos son, salvo que se indique lo contrario, públicos, mientras que en una class son privados... por eso en una class es habitual encontrar public:... es la única forma de generar la interfaz pública de la clase.

Cita:
Iniciado por dmorill Ver Mensaje
El código es super largo pero pondré lo respecto a la pregunta, si quieres el resto solo de dices y con gusto lo pongo todo. Te describo lo que estaba haciendo...
Lo siento, sigo sin entender para qué necesitas la función nextVecino. Tal vez si pones el código completo para intentar entender el uso que pretendes darle...

Un saludo.
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #10 (permalink)  
Antiguo 14/02/2016, 18:03
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 9 años, 10 meses
Puntos: 1
Respuesta: Incrementar un iterador de un vector<MyClass>

Hola gracias por tus respuestas. Entiendo lo que me dices respecto a la inicialización de variables, pero la asignación es otra cosa. En el main pones Punto p = 1;, ésto no debería funcionar por si sola, p es una estructura y amenos que no sobrecargues el operador = debería ser un error.

Cita:
Iniciado por eferion Ver Mensaje
Lo siento, sigo sin entender para qué necesitas la función nextVecino. Tal vez si pones el código completo para intentar entender el uso que pretendes darle...
Un saludo.
Te anexo el codigo al final, pero intentaré darme a entender antes, porque creo que leer un código no es fácil, y entender que se hace exactamente es difícil.

Básicamente es un branch and boud. La cuestión es que tengo un arreglo de nodos que empieza vacío y debo recorrer todos los nodos sin repetirlos pero no en orden (1,2,3,4,...) sino que según sus vecinos, por ejemplo primero nodo 1 luego al vecino de él que puede ser el 2, y luego un vecino de éste ultimo que es el 5 y así, luego cuando ya no hay más nodos me regreso al nodo anterior y elijo el próximo vecino de él. eje teníamos un vector así 1,2,5,4,6 entonces regreso al nodo anterior el 4 y veo cual es el siguiente vecino además del 6, en éste ejemplo no habrá otro vecino así que regreso al 5 y veo otro vecino del 5 que puede ser el 6 y luego avanzo y así. Todo esto para recorrer todos los caminos posibles de los vecinos conectados.

Código: forosdelweb me dice que es muy largo para copiarlo jeje, te lo paso por mega:

header(.h):
link1
sourse(.cpp):
link2

Saludos,
  #11 (permalink)  
Antiguo 14/02/2016, 18:28
Avatar de xKuZz  
Fecha de Ingreso: febrero-2015
Ubicación: nullptr
Mensajes: 183
Antigüedad: 9 años, 2 meses
Puntos: 27
Respuesta: Incrementar un iterador de un vector<MyClass>

Poner Punto p = 1 es una manera implícita de llamar a constructor que te ha puesto eferion, es decir equivale a Punto p(1), que a su vez equivale a Punto p(1,0) puesto que el valor de x fue proporcionado (1) x valdrá 1 e y valdrá 0.

Con el fin de evitar que esta conversión implícita existe la palabra reservada explicit.

Ten en cuenta que poner el operador = en la línea en la que declaras la variable es llamar a un constructor.

Código C++:
Ver original
  1. Punto p = 1; // Constructor
Código C++:
Ver original
  1. Punto p; // Reserva de memoria para la estructura
  2. p=0; // operador de asignación =
  #12 (permalink)  
Antiguo 15/02/2016, 02:51
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 7 meses
Puntos: 204
Respuesta: Incrementar un iterador de un vector<MyClass>

Código C++:
Ver original
  1. bool siEstaEnBlock(vector<int>& v, int i);
  2. bool siEstaEnBlock(vector<int>& v, int i){

Las funciones únicamente hay que declararlas si las mismas son llamadas en algún punto anterior a su implementación. Si no se da el caso la declaración sobra:

Código C++:
Ver original
  1. void funcOk1(); // Declaracion
  2.  
  3. void funcOk2() // No hay declaración
  4. { return; }
  5.  
  6. int main()
  7. {
  8.   funcOk1(); // Compila porque la declaración se encuentra antes que esta línea
  9.   funcOk2(); // Compila porque la implementación se encuentra antes que esta línea
  10.   funcNoOk(); // Llegados a este punto el compilador no encuentra ni declaración ni compilación -> ERROR
  11. }
  12.  
  13. void funcOk1()
  14. { return; }
  15.  
  16. void funcNoOk()
  17. { return; }

Si una declaración sobra lo mejor es eliminarla porque no va a aportar absolutamente nada salvo problemas ¿Qué clase de problemas? Bueno, imagínate que cambias la firma de la función y te olvidas de actualizar la declaración... empezarás a recibir errores al linkar el código que te pueden hacer perder bastante tiempo... eso si no da la mala suerte que hay otra función exactamente igual en otro sitio y acabas llamando a la misma... que se puede dar el caso.

Código C++:
Ver original
  1. vector<int>::iterator itSigue;
  2. while (vStack.empty()  == false){        //recorre una componente
  3.   itSigue = nextVecino(vit.back(), Vertex[vStack.back() - 1].get_adjaList(), itNULL);
  4.   ...
  5. }

¿Ese código no es equivalente a este otro?

Código C++:
Ver original
  1. auto itSigue = Vertex[vStack.back() - 1].get_adjaList();
  2. while (vStack.empty()  == false){        //recorre una componente
  3.   ...
  4.   ++itSigue;
  5. }

Otra equivalencia:

Código C++:
Ver original
  1. bool siEstaEnBlock(vector<int>& v, int i){
  2.     vector<int>::iterator it = v.begin();
  3.     for (; it != v.end(); it++) {
  4.         if (i == *it) return true;
  5.     }
  6.     return false;
  7. }
  8.  
  9. bool siEstaEnBlock(const vector<int>& v, int i)
  10. {
  11.   auto it = std::find(v.begin(),v.end(),i);
  12.   return it != v.end();
  13. }

Por otro lado... ¿Por qué pones toda la implementación de la clase en la cabecera?? Parece una chorrada pero las implementaciones no deberían ir en la cabecera salvo casos muy puntuales... si en algun momento trabajas con proyectos más grandes verás la diferencia en los tiempos de compilación y en el mantenimiento.

Un saludo
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #13 (permalink)  
Antiguo 15/02/2016, 08:44
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 9 años, 10 meses
Puntos: 1
Respuesta: Incrementar un iterador de un vector<MyClass>

Hola,

Cita:
Iniciado por eferion Ver Mensaje
Código C++:
Ver original
  1. bool siEstaEnBlock(vector<int>& v, int i);
  2. bool siEstaEnBlock(vector<int>& v, int i){

Las funciones únicamente hay que declararlas si las mismas son llamadas en algún punto anterior a su implementación. Si no se da el caso la declaración sobra:
Gracias lo tendré en cuenta, pero como estoy aprendiendo, y he visto el tema de las declaraciones e implementaciones, se me ha quedado grabado jeje. Una pregunta si no son necesarias esas declaraciones si las implemento antes de usarlas en el código, para que se utilizan?

Cita:
Iniciado por eferion Ver Mensaje
Código C++:
Ver original
  1. vector<int>::iterator itSigue;
  2. while (vStack.empty()  == false){        //recorre una componente
  3.   itSigue = nextVecino(vit.back(), Vertex[vStack.back() - 1].get_adjaList(), itNULL);
  4.   ...
  5. }
¿Ese código no es equivalente a este otro?
Código C++:
Ver original
  1. auto itSigue = Vertex[vStack.back() - 1].get_adjaList();
  2. while (vStack.empty()  == false){        //recorre una componente
  3.   ...
  4.   ++itSigue;
  5. }
Realmente no, la mía devuelve un iterador la tuya un vector, pues .get_adjaList(); retorna la lista de nodos adyacentes. Pero lo más importante es que el mio está dentro del while, eso es necesario porque hay que almacenar en que vecino está y seguir avanzando. Ejemplo del grafo b de que adjunte antes:

La siguiente gráfica es una captura dentro del while, los datos a la iz, y a la derecha el stack que es donde voy comprobando si hay o no hay ciclo, pos es la posición de la lista de vecinos, por ejempo el nodo 3 vamos al 4 que es la posición 3 de los vecinos de nodo 3 (1,2,4,6) (contando desde 1 jeje no desce 0 para el ejemplo).



Mi stack tiene los nodos 1-5-2-3-4 e imagina que regreso del nodo 4 al nodo 3, para seguir recorriendo el grafo debo seguir en el siguiente vecino del 3, de la posición 3 a la posición 4 es decir de vecino 4 a vecino 6. y esto para cada nodo por tanto hay que guardar en que vecino está cada nodo. No es que se recorra todos los vecinos de una vez. sino que se crea un árbol de todas las combinaciones de nodos.

Cita:
Iniciado por eferion Ver Mensaje
Otra equivalencia:

Código C++:
Ver original
  1. bool siEstaEnBlock(vector<int>& v, int i){
  2.     vector<int>::iterator it = v.begin();
  3.     for (; it != v.end(); it++) {
  4.         if (i == *it) return true;
  5.     }
  6.     return false;
  7. }
  8.  
  9. bool siEstaEnBlock(const vector<int>& v, int i)
  10. {
  11.   auto it = std::find(v.begin(),v.end(),i);
  12.   return it != v.end();
  13. }
jeje, debo estudiar más esa programación. Pues no sabía de esa función find. No me gusta usar mucho los auto, al menos hasta que maneje mejor c++, pues así me ayuda a comprender mejor el código sabiendo bien que devuelve cada función.

Cita:
Iniciado por eferion Ver Mensaje
Por otro lado... ¿Por qué pones toda la implementación de la clase en la cabecera?? Parece una chorrada pero las implementaciones no deberían ir en la cabecera salvo casos muy puntuales... si en algun momento trabajas con proyectos más grandes verás la diferencia en los tiempos de compilación y en el mantenimiento.
Te refieres a que implemento algunas funciones inline, dentro de la clase? así:

Código C++:
Ver original
  1. void set_blockedmap(int i){ _blockedmap.push_back(i); }

O a funciones miembro, que los implemento fuera de la clase pero en la cabecera, ejemplo:

Código C++:
Ver original
  1. InfoNodo::InfoNodo(int id, vector<int> adjaList){
  2.     _id = id;
  3.     _adjaList = adjaList;
  4.     _stack = false;
  5.     _blocked = false;
  6.     _siSoyCiclo = false;
  7. }

Donde se deben hacer dichas implementaciones?

Saludo

Etiquetas: incrementar
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 23:10.