Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: alpachino98 en 20 Enero 2019, 01:28 am



Título: Como intercambiar valores de un iterator en C++
Publicado por: alpachino98 en 20 Enero 2019, 01:28 am
Hola,
Si alguien puede ayudarme en como intercambiar dos valores de una lista (debe estar ordenada alfabéticamente). Esta lista contiene objetos de una clase contacto.
Les dejo por aquí los atributos de la clase contactos.
Código:
private:
  string nombre;// Cadena con el contenido de la palabra
  int nTlf;

Atributos de la clase Agenda ( es la que contiene la lista).
Código:
 private:
  list<Contacto> listapal;// La lista STL con los contactos de la agenda.
  bool encuentraContacto(const string &, list<Contacto>::iterator &);
 
Este es el método que debe ordenar la lista <listapal>.
Código:
void/*list<Contacto>*/ Agenda:: ordenaListas()
{
list<Contacto> :: iterator aux;
Contacto contI;
Contacto contJ;

for (list<Contacto>::iterator it=listapal.begin();it != listapal.end(); it++)
{
for (list<Contacto>::iterator jt=it;jt!=listapal.end(); jt++)
{
jt++;
contI=*it;
contJ=*jt;

if(!(contI.getNombre()<=contJ.getNombre()))
{
//Primer intento
/* *aux=*it;
*it=*jt;
*jt=*aux;*/

                                //Segundo intento
pos->setNombre(it->getNombre());
pos->setNumeroTelefono(it->getNumeroTelefono());

it->setNombre(jt->getNombre());
it->setNumeroTelefono(jt->getNumeroTelefono());

jt->setNombre(pos->getNombre());
jt->setNumeroTelefono(pos->getNumeroTelefono());

}
}}

Les he puesto las dos cosas que creo que más sentido tendrían. El problema es que al compilar y ejecutarlo se queda congelado cuando intenta ordenar la lista.

Lo que pretendo hacer es usar el método de la burbuja. Les dejo una idea de lo que quisiera hacer pero con iteradores.
Código:
for (i=0; i<n-1; i++)
{
   for (j=i+1; j<n; j++)
  {
    if(V[i]>V[j])
    {
     aux = V[i];
     V[i] = V[j];
     V[j] = aux;
    }
  }
}




Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: Loretz en 20 Enero 2019, 01:54 am
Mira, yo creo que en tu segundo for...
cuando haces:
Código:
jt=it;
estás asignando it a jt, y entonces, con
Código:
jt++;
lo que haces es incrementar los dos, jt apuntará al siguiente elemento en la lista, igual que it.

Luego...
Código:
if(!(contI.getNombre()<=contJ.getNombre()))
jamás se cumplirá porque contI y contJ son iguales (mismo nombre, mismo nTlf).

Y como esa condición jamás se cumple, el ciclo for interno va incrementando a jt (y también a it, que es el mismo iterador) hasta hacerlos igual a listapal.end().

Y ahora que it es == a listapal.end(), cuando vuelve a ejecutarse el incremento del ciclo for externo, it va a ser el siguiente de listapal.end() (undefined behavior), que va a cumplir con la condición
Código:
it != listapal.end();
porque efectivamente es distinto, es uno más allá que más allá del último.

¿Y entonces? El ciclo for más externo jamás se detendrá... (bueno, sí se detendrá, más temprano que tarde el sistema operativo se encargará de humillarte convenientemente).

Pregunta. La std::list ya tiene su método sort(), ¿por qué no lo usas?



Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: alpachino98 en 20 Enero 2019, 02:02 am
Vale ahora miraré como puedo solucionar todo eso que me has dicho, no había caído en todo eso que has dicho y creo que tienes toda la razón.
Uso este método y no el de sort porque sort no puede ordenar objetos de la clase contacto, no he encontrado ninguna manera de decirle que quiero que lo ordene segun el atributo nombre. Si sabes algún modo de hacerlo, en plan operador ternario al que se le pasa la condición o algo asi.

Muchas gracias.


Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: Loretz en 20 Enero 2019, 03:48 am
Un ejemplo con el estándar C++17

Código:
#include <iostream>
#include <string>
#include <list>

struct Contacto {
    std::string nombre;
    int nTlf;
};

void mostrar(const std::list<Contacto>& Agenda)
{
    for (const auto& [n, t] : Agenda) {
        std::cout << n << " -> " << t << '\n';
    }
    std::cout << '\n';
}

int main()
{
    std::list<Contacto> Agenda{ {"Juan", 14}, {"Ana", 23}, {"Susana", 19}, {"Abel", 22} };
    std::cout << "desordenada:\n";
    mostrar(Agenda);

    // ordenada por nombre:
    Agenda.sort([](const auto& c1, const auto& c2) { return c1.nombre < c2.nombre; });
    std::cout << "ordenada:\n";
    mostrar(Agenda);
}


Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: alpachino98 en 20 Enero 2019, 12:30 pm
No se si es que no lo he entedido mucho pero si hago :
Código:
listapal.sort([](const Contacto& c1, const Contacto& c2) { return c1.nombre <c2.nombre; });

Me da error:
Código:
	[Error] no matching function for call to 'std::list<Contacto>::sort(Agenda::imprimeAgenda()::<lambda(const Contacto&, const Contacto&)>)'

Y si lo hago tal cual lo tienes tu:
Código:
listapal.sort([](const auto& c1, const auto& c2) { return c1.nombre <c2.nombre; });

Me da estos errores:
Código:
	[Error] request for member 'nombre' in 'c1', which is of non-class type 'const int'

Código:
Error] request for member 'nombre' in 'c2', which is of non- type 'const int'

Código:
	[Error] no matching function for call to 'std::list<Contacto>::sort(Agenda::imprimeAgenda()::<lambda(const int&, const int&)>)'

Y eso que he cambiado el atributo nombre a publico(ya se que no debería pero es que con el getNombre() me dice que no existe getNombre()).
Pero es que tu código tampoco me funciona, no entiendo nada. No debe ser tan complicado :-\


Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: Loretz en 20 Enero 2019, 13:43 pm

Código:
listapal.sort([](const Contacto& c1, const Contacto& c2) { return c1.nombre <c2.nombre; });
Generic lambda está a partir del estándar C++14:
https://isocpp.org/wiki/faq/cpp14-language#generic-lambdas (https://isocpp.org/wiki/faq/cpp14-language#generic-lambdas)



Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: alpachino98 en 20 Enero 2019, 14:04 pm
En visual studio si me funciona pero prefiero hacerlo sin la lambda, en java hemos visto lambdas pero el estandar que usamos es c++ 11. He cambiado eso de adelantar el iterador jt y he cambiado la condicion del if. Sigue sin funcionar pero ya no se que hacer, supongo que lo dejare sin ordenar o lo pondré todo en una misma clase ( aunque prefería hacerlo en dos).

Código:
for (list<Contacto>::iterator it=listapal.begin();it != listapal.end(); it++)
{
for (list<Contacto>::iterator jt=it;jt!=listapal.end()--; jt++)
{
//jt++;
contI=*it;
contJ=*jt;

if(contI.getNombre().compare(contJ.getNombre())>0)
{

/* *pos=*it;
*it=*jt;
*jt=*pos;*/

pos->setNombre(it->getNombre());
pos->setNumeroTelefono(it->getNumeroTelefono());

it->setNombre(jt->getNombre());
it->setNumeroTelefono(jt->getNumeroTelefono());

jt->setNombre(pos->getNombre());
jt->setNumeroTelefono(pos->getNumeroTelefono());


}


Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: K-YreX en 20 Enero 2019, 16:15 pm
La buena noticia es que es posible hacer lo que quieres hacer, el único problema es que estabas teniendo un problema con el uso de los iteradores. Si asignas un iterador a otro e incrementas uno de ellos, no se incrementa el otro (solo lo aclaro)

Código
  1. void Agenda::ordenar(){
  2.    Contacto auxiliar;
  3.    for(list<Contacto>::iterator it1 = listapal.begin(); it1 != --listapal.end(); it1++)
  4.        for(list<Contacto>::iterator it2 = ++it1--; it2 != listapal.end(); it2++)
  5.            if(it1->getNombre() > it2->getNombre()){
  6.                auxiliar = *it1;
  7.                *it1 = *it2;
  8.                *it2 = auxiliar;
  9.            }
  10. }

Ya sé que puede parecer muy raro eso de ++it1-- pero es la forma más rápido de hacerlo. Te lo explico por si lo quieres hacer de otra forma:
Lo normal es que si el bucle externo se controla con <i>, el bucle interno empieza siempre en <i+1>. Al ser iteradores de una lista no se puede hacer <it2 = it1+1>... Entonces cómo lo arreglamos? Con el uso de los incrementos en prefijo y sufijo:
  • En prefijo primero se incrementa/decrementa y luego se asigna.
  • En sufijo primero se asigna y luego se incrementa/decrementa.
Entonces así conseguimos incrementar <it1> en 1, después se asigna a <it2> y después se decrementa <it1> para dejarlo igual. Suerte :-X


Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: alpachino98 en 20 Enero 2019, 17:57 pm
Vale muchas gracias, es verdad, me he mareado un poco ahí con los iteradores. He probado tu código y sigo con el mismo problema, el resto del programa funciona correctamente pero ya no se si puede que se quede pillado por otra cosa o que...el caso es que cada vez que intenta ordenar se queda congelado hasta que el sistema operativo decide mandarlo a la *****.  >:D


Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: K-YreX en 20 Enero 2019, 17:59 pm
Yo he creado un programa similar al que mostrabas pero más sencillo para probar el ordenamiento y me ha funcionado sin problemas. Deja tu código a ver cómo te ha quedado por si el fallo está en otra cosa. :-X


Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: alpachino98 en 20 Enero 2019, 18:19 pm
Paso el codigo por un enlace de mega. Siento mucho el estado del codigo, hay mucho comentario pero es por los intentos. El problema se encuentra en la clase Agenda, todos los demas metodos funcionan bien excepto <ordenaListas()>

Citar
https://mega.nz/#!V19hjQbQ!JsclFGwKgG1TKwaoY5e6UdJw2d7MihIuUvjYUxSFJW0


Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: K-YreX en 20 Enero 2019, 18:26 pm
El problema era que olvidé los paréntesis. (++it1)--.
Prueba a ver si funciona y me cuentas. Suerte :-X


Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: alpachino98 en 20 Enero 2019, 18:50 pm
Justo, mil gracias!!

Dejo el código para intercambiar iteradores y ordenar con iteradores una lista alfabeticamente por si le sirve a alguien.

Código:
void Agenda::ordenaListas(){
    Contacto auxiliar;
    for(list<Contacto>::iterator it1 = listapal.begin(); it1 != --listapal.end(); it1++)
        for(list<Contacto>::iterator it2 = (++it1)--; it2 != listapal.end(); it2++)
            if(it1->comparar(it2->getNombre()))
{
                auxiliar = *it1;
                *it1 = *it2;
                *it2 = auxiliar;
            }
}

Código:
bool Contacto::comparar(const string& s)
{
int i=0;
while (i<nombre.length()||i<s.length())
if(nombre[i]!=','&& s[i]!=',')
if(nombre[i]!='\0'&&s[i]!='\0')
{

if(s[i]<nombre[i])
return true;
if(s[i]==nombre[i])
i++;
else
return false;
}
}


Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: Loretz en 21 Enero 2019, 19:16 pm
En C++11 también puedes usar el miembro sort de una std::list; hay varias formas, creo que la más usual es:

1) Defines el criterio de ordenación:
Código:
struct Orden {
    bool operator() (const Contacto& c1, const Contacto& c2)
    {
        return c1.nombre < c2.nombre;
    }
};
2) Lo usas en sort:
Citar
Agenda.sort(Orden());


Título: Re: Como intercambiar valores de un iterator en C++
Publicado por: dijsktra en 22 Enero 2019, 16:02 pm
Justo, mil gracias!!

Dejo el código para intercambiar iteradores y ordenar con iteradores una lista alfabeticamente por si le sirve a alguien.

Código:
void Agenda::ordenaListas(){
    Contacto auxiliar;
    for(list<Contacto>::iterator it1 = listapal.begin(); it1 != --listapal.end(); it1++)
        for(list<Contacto>::iterator it2 = (++it1)--; it2 != listapal.end(); it2++)
            if(it1->comparar(it2->getNombre()))
{
                auxiliar = *it1;
                *it1 = *it2;
                *it2 = auxiliar;
            }
}
...
Más simple  (y más C++). Usa el operador ">" de strings, los operadores next(),begin() de iterator, y swap!!
Código
  1. #include <string>
  2. #include <utility> //swap
  3. #include <list> //list
  4.  
  5. using namespace std;
  6. ...
  7. // BubleSort
  8. // P : listapal=A[0..N) N>=0
  9. // Q : \forall i : 0 <= i < N-1: V[i] < V[i+1] and permut(listapal,A,N)
  10. void  Agenda::ordenaListas()
  11. {
  12.  list<Contacto>::iterator it1, it2;
  13.  for( it1 = listapal.begin(); it1 != prev(listapal.end()); it1++)
  14.    for(it2 = next(it1); it2 != listapal.end(); it2++)
  15.      if (it1->getNombre() > it2->getNombre()) swap(*it1,*it2);
  16. }
  17.  
No puedo mandar resultados porque no tengo el resto de los componentes, (Contacto, Agenda)...
 Fijaos que es una traducción del clasico en pseudocodigo.
En algún sitio he leído que hay que tener cuidado con usar el operador "<" en iteradores!. Tiene que estar definido por el programador del iterador!
Código
  1. for (i=0; i<n-1; i++)
  2.   for (j=i+1; j<n; j++)
  3.    if(V[i]>V[j])
  4.       V[i],V[j]=V[j],V[i]; // Esto no existe en C
  5.