Domanda Risolto Stampare contenuto lista senza ripetizioni

ellosma

Utente Iron
9 Settembre 2021
9
1
0
5
Ultima modifica:
avendo una lista contenente item fatti così : <anno , tempo , nome , luogo> , devo scrivere un codice che per ciascun <luogo> presente nella lista L1 (senza ripetizioni) stampi il numero totale di atleti che hanno quel <luogo> come attributo. Si possono utilizzare liste di appoggio , quindi io ho creato una lista L2 ( inizialmente vuota ) di tipo conteggio , cioè’ fatta di item ( che ho chiamato conteggio ) nel formato ( luogo , count ). Dove count e’ la somma delle gare disputate in quel luogo.

per esempio avendo L1 < (2009,9.58,Usain Bolt,Berlin) (2012,9.63,Usain Bolt,London) (2008,9.69,Usain Bolt,Beijing) (2007,9.74,Asafa
Powell,Rieti) (2014,9.56,Usain Bolt,London)| >

dovrei ottenere
London 2
Beijing 1
Berlin 1
Rieti 1

mentre io ottengo
London 1
London 1
Beijing 1
Berlin 1
Rieti 1

C++:
Item it1;
    conteggio it2;
    conteggio i;
    conteggio l;
    int conta = 0;
    LList<conteggio> L2;
    string luogo;


  
    for (L1.moveToStart(), L2.moveToStart(); L1.currPos() < L1.length() && L2.currPos() < L2.length(); L1.next(), L2.next()) {
        it1 = L1.getValue();
        it2 = L2.getValue();

            if (it1.getluogo() == it2.getluogo()) 
            {
                luogo = it1.getluogo();
                conta = conta + 1 ;
              
            }
            else {
                luogo = it1.getluogo();
                conta = 1  ;
              
              
              
            }

                                conteggio i(luogo, conta);
                L2.insert(i);


        }
  
for (; L1.currPos() < L1.length(); L1.next()) {
        it1 = L1.getValue();
        luogo = it1.getluogo();
        if (it1.getluogo() == it2.getluogo())
        {
            conta = conta + 1;
      
        }
        else { conta = 1; }
        conteggio i(luogo, conta);
        L2.append(i);
    }
 
Per semplificare la lettura del codice usa il tag CODE che mantiene gli spazi e colora la sintassi.
Se ho capito bene il tuo problema è che London appare due volte, il motivo è semplice e si nota subito: in uno la L è maiuscola mentre nell'altro no. Usando l'operatore == risulteranno stringhe diverse, ti serve una funzione come stricmp (C), equals (STL C++) oppure potresti anche implementarla tu.
 
la questione della lettera e’ relativo ad un errore di battitura che ho fatto io nell’ esporre il mio quesito. ho trascritto l’outout senza copia e incolla e ho dimenticato la maiuscola.Purtroppo e’ una questione di codice che non so come risolvere..
 
Ultima modifica:
Il ciclo for è sbagliato. Non devi iterare contemporaneamente sulle due liste, devi iterare su una e cercare/inserire nell'altra. Tenendo buona la tua idea, quindi senza usare dizionari o altre strutture dati, io l'ho risolto in questo modo:
C++:
std::vector<Count> counter;
for (const auto &elm : entries) {
  auto it = std::find_if(std::begin(counter), std::end(counter),
                         [&elm](const Count &cnt) { return elm.place == cnt.place; });

  if (it == std::cend(counter)) {
    counter.push_back(Count(elm.place));
  } else {
    it->increment();
  }
}

Ho usato std::vector dove tu hai usato la tua implementazione (non pubblicata) di linked list, ma l'idea di fondo è la stessa: per ogni elemento nella lista controlli se è già presente nella lista di appoggio counter; se non lo trovi lo inserisci (prima occorrenza), altrimenti incrementi il suo numero di occorrenze.

Immagino che il tuo sia un esercizio scolastico, quindi non credo ti sia possibile utilizzare tutto ciò che nel mio codice inizia per std, ma non dovrebbe essere difficile implementare qualcosa di equivalente per la tua LList. Un modo stupido e non generalizzato per implementare quello che io faccio con std::find_if potrebbe essere quello di creare una funzione Count *find_by_place(LList<Count> &counter, std::string place) che restituisce NULL quando l'elemento non c'è.

Se vuoi aiuto riguardo al tuo codice, devi postare qualcosa che posso copia-incollare, compilare ed eseguire. Capisco che magari lo vuoi tenere privato visto che si tratta di un lavoro scolastico, ma devi metterci in condizione di poterti aiutare agevolmente. Sei tu che chiedi aiuto, quindi non siamo noi a doverci arrampicare sugli specchi per decifrare uno spezzone di codice incompleto. In ogni caso, già si vede che il problema è dentro le parentesi tonde dell'for: non devi iterare contemporaneamente su due liste diverse; se non usi la funzione di appoggio che ti ho suggerito di implementare, devi scrivere un for dentro un for.
 
hai perfettamente ragione, purtroppo non posso pubblicare il codice completo perche‘ si basa su file scritti dal mio professore negli anni, quindi posto solamente il codice nel main o nell’item , che invece ho scritto io. mi rendo conto che per voi sia difficile aiutarmi in queste condizioni. purtroppo Non credo di avere ancora le Conoscenze necessarie per poter capire il tuo codice. Pero’ leggendo quello che mi hai scritto ho pensato di utilizzare la funzione find , non nel ciclo for , bensì direttamente nella seconda lista. Dato che l’output finale era parzialmente corretto , su quello dovrei cercare se ci sono item ( di tipo conteggio ) doppi e settare il campo totale dell’item , altrimenti andare avanti. Quello che ho scritto io l’ho tradotto in questo codice, ed ora sono effettivamente riuscita a rimuovere i doppioni però :
1) non mi considera il primo elemento
2) non riesco ad aggiornare il campo totale ( parte commentata )

C++:
for (L2.moveToStart() ; L2.currPos() < L2.length(); L2.next()) {

        it2 = L2.getValue();

        int trovato = find(L2, it2);


        if (trovato != -1) {

            L2.moveToPos(trovato); // mi posiziono nel punto dove ho trovato doppione

            conteggio removed = L2.getValue(); // estraggo item da aggiornare

            //removed.settotale( conta );
            L2.remove();
            //L2.insert(removed);
            L1.next();
          
        }

        else L2.next();



    }


    cout << endl;
    cout << " LISTA L2: " << endl;

    lprint(L2);

la funzione find ci e’ stata mostrata a lezione, io l’ho solo adattata alla lista e all’item di tipo conteggio


C++:
int find(List<conteggio>& L, conteggio& item_to_search) {
    conteggio it;
    for (L.moveToStart(); L.currPos()<L.length(); L.next()) {
        it = L.getValue();
        if (item_to_search.getluogo() == it.getluogo()) return L.currPos();  // Found K
    }
    return -1;                // K not found
}

mi rendo conto Che nemmeno questo e’ un esempio che compila , spero comunque di avervi ridotto almeno un po’ ;a lettura del codice , agendo direttamente su L2 e lasciando intatto il resto del codice scritto. Nel caso non riusciate ad aiutarmi grazie comunque per l’idea del find, se non altro ho fatto un leggero progresso dato che ora almeno gli item doppi li toglie..
 
Meglio, anche se mancano ancora pezzi fondamentali per poterti aiutare. Mi spiego meglio.

Per come te l'ho spiegata io prima, counter (da te chiamata L2) parte vuoto e si riempie man mano che scorri gli elementi nella lista con il ciclo for. Io leggo il tuo codice, assumo che L2 sia inizialmente vuoto e quello che capisco è:
C++:
for (L2.moveToStart() ; L2.currPos() < L2.length(); L2.next()) {
  // sono alla prima iterazione del ciclo for e it2 è il primo elemento nella lista
  it2 = L2.getValue();

  // cerco it2 in L2, ma ovviamente non lo trovo perché L2 è vuoto
  int trovato = find(L2, it2);

  // non entro in questo if, perché l'elemento non l'ho trovato
  if (trovato != -1) {
    // ... roba che non faccio
  }
  else L2.next(); // <- faccio questo
}
quindi, bear with me, da quel che leggo il tuo codice dovrebbe andare in errore subito perché stai cercando di andare al prossimo elemento (L2.next()) in una lista vuota. La lista L2 non si riempirà mai perché, nel codice che mi stai mostrando, non c'è alcuna operazione di inserimento. Al posto di else L2.next(); devi mettere l'inserimento di un nuovo elemento (con occorrenza = 1) nel contatore. Oltre a questo, a me sembra (dico sembra perché non ho abbastanza codice per poter giudicare) che ci siano errori anche all'interno dell'if; ma a quello ci pensiamo dopo. Adesso sono solo interessato a capire dove e come avviene l'inserimento di un elemento in L2.

Se vuoi io ti posto anche una soluzione completa, ma devi dirmi tu se ti può aiutare o se preferisci guardare solo il tuo codice per non confonderti.
 
quello Che dici e’ giustissimo , infatti ho provato a correggere l’errore del primo codice postato inserendo questo codice, quello con l’if e non aveva senso. Quello che ho cercato di fare dopo era , dopo aver stampato L2 con le ripetizioni , di ristamparlo una seconda volta , questa volta senza. Spero di essermi spiegata , almeno un po’.



La mia idea era questa:



1) faccio partire il codice ( la prima parte che ho postato ) e L2 viene riempita con questi item

Beijing 1

London 1

London 1

Berlin 1

Rieti 1

(questo e’ L’ output che ottenevo con il primo codice postato)



da qui , non riuscendo a correggere direttamente l’errore , volevo sfruttare la find e , solo in un secondo tempo rimuovere doppioni e aggiornare contatore



2) Ora , piuttosto che correggerlo , l’ho continuato e ( utilizzando il secondo codice , che non e’ altro che il continuo del primo) ho cercato di eliminare da qui gli item dopp. In questo modo ottengo

London 1

Berlin 1

Rieti 1



mi rendo conto che a livello di programmazione la mia idea e’ un abominio e per nulla efficiente , volevo solo cercare di arrivare al Risultato anche utilizzando 50 righe di codice in più.. sicuramente facevo prima a correggere il primo codice ma Almeno mi sono avvicinata un altro po a quello che dovrebbe essere L’ output


allego il main completo , grazie mille in ogni caso per l'aiuto
 

Allegati

  • esercizio liste.txt
    4.7 KB · Visualizzazioni: 4
Ultima modifica:
quello Che dici e’ giustissimo , infatti ho provato a correggere l’errore del primo codice postato inserendo questo codice, quello con l’if e non aveva senso. Quello che ho cercato di fare dopo era , dopo aver stampato L2 con le ripetizioni , di ristamparlo una seconda volta , questa volta senza. Spero di essermi spiegata , almeno un po’.
Uhm, no... non ho capito. In ogni caso, errare è umano e perseverare è diabolico. Ti ho segnalato che c'è un errore e ti ho mostrato che la soluzione si può scrivere in non più di 10 righe di codice, quindi non ostinarti a raddrizzare qualcosa di nato storto. Grossomodo dovrai scrivere qualcosa del genere:
C++:
// Linea 160, dopo Albero.show(cout)
LList<conteggio> L2;
for (L1.moveToStart(); L1.currPos() < L1.length(); L1.next()) {
    string luogo = L1.getValue().getluogo();

    // implemento la find
    for (L2.moveToStart() ; L2.currPos() < L2.length(); L2.next()) {
        if (luogo == L2.getValue().getluogo()) break;
    }

    // controllo se la find ha trovato l'elemento
    if (L2.currPos() < L2.length()) {
        // non ho abbastanza codice per sapere cosa scrivere, ma più o meno sarà
        L2.getValue().incrementa();
        // oppure:
        // conteggio cnt = L2.getValue();
        // cnt.incrementaConteggio();
        // L2.setValue(cnt);
    } else {
        L2.append(conteggio(luogo, 1));
    }
}

// stampa i valori di L2
lprint(L2);
Cancella tutto il resto. Dalla linea 160 in su fai cose che non hanno niente a che fare con L2, quindi okay, ma dopo il codice che ti ho scritto L2 non lo devi più modificare.

Nota: il contenuto all'interno dell'if poterbbe non funzionare a causa dell'implementazione di getValue o dell'implementazione della classe conteggio. Se vuoi più aiuto, devi fornire quella parte del codice.
 
allego sia tutto il codice del main sia tutto ilcodice delle classi item ,che ho scritto io. spero possa aiutare perchè non riesco proprio a saltarci fuori
 

Allegati

  • atleti.txt
    951 bytes · Visualizzazioni: 4
  • item.txt
    1.9 KB · Visualizzazioni: 4
  • Llistmain.txt
    4.9 KB · Visualizzazioni: 5
All'interno della classe conteggio aggiungi il metodo
C++:
void incrementa() { totale++; }
e poi la soluzione a ciò che chiedi nell'open post è
C++:
LList<conteggio> L2;
for (L1.moveToStart(); L1.currPos() < L1.length(); L1.next()) {
    string luogo = L1.getValue().getluogo();

    for (L2.moveToStart() ; L2.currPos() < L2.length(); L2.next()) {
        if (luogo == L2.getValue().getluogo()) break;
    }

    if (L2.currPos() < L2.length()) {
        conteggio cnt = L2.getValue();
        cnt.incrementa();
        L2.setValue(cnt);
    } else {
        L2.append(conteggio(luogo, 1));
    }
}
ammesso che esista un metodo setValue all'interno della classe LList, cosa che io posso solo provare a indovinare. Tutto quello che nel tuo codice va da linea 168 fino a linea 283 lo devi cancellare.
 
all'interno di llist , nella classe conteggio , esiste un settotale. ma l'unico errore che mida è proprio quello,mi dice che conteggio non include un membro con settotale, invece io l'ho messo . tra i file che ci sono forniti dal corso , invece , ci sono metodi standard che ci è stato detto di non modificare , ma tra questi cè getValue ma non setValue.
 
Il settotale è all'interno di conteggio, non all'interno di LList. Io non ho usato settotale, anzi... ti consiglierei di rimuoverlo, tanto è un contatore e le uniche operazioni sensate sono incrementi e decrementi. Se in LList non c'è setValue ho bisogno di vedere cosa c'è e come funziona.
 
  • Mi piace
Reazioni: ellosma
Ultima modifica:
all'interno di llist,i metodi pubblici che si possono utilizzare sono:

insert

append

// per rimuovere e ritornare elemeno corrente
remove()

moveToStart() // posiziona il cursore a inizio lista


moveToEnd() // posiziona il cursore a fine lista


// posiziona il cursore una posizione a sinistra, non effettua cambiamenti se sitrova già all'inizio
prev()

// sposta il cursore un passo a destra , non effettua cambiamenti se si trova già alla fine
next()


length() // ritorna la lunghezza lista

//ritorna la posizione corrente del cursore
currPos()

// muove "giu" la lista in posizione pos
moveToPos

getValue() // ritorna elemento corrente


reverse() { // inverte la lista
 
C++:
LList<conteggio> L2;
for (L1.moveToStart(); L1.currPos() < L1.length(); L1.next()) {
    string luogo = L1.getValue().getluogo();

    for (L2.moveToStart() ; L2.currPos() < L2.length(); L2.next()) {
        if (luogo == L2.getValue().getluogo()) break;
    }

    if (L2.currPos() < L2.length()) {
        conteggio cnt = L2.remove();
        cnt.incrementa();
        L2.append(cnt);
    } else {
        L2.append(conteggio(luogo, 1));
    }
}
 
ora funziona tutto correttamente,non so davvero come rigraziarti, soprattutto per l'infinita pazienza che hai avuto! posso chiederti se ho capito bene tutti i passaggi che hai fatto ? vorrei rifarlo e cercare di capire bene lo svolgimento ,se ci riesco.

1) scorro la lista L1 , di tipo item , dall'inizio alla fine ed estraggo di volta in volta i campi 'luogo' di ogni item
2) scorro la lista l2 , dall'inizio alla fine , e se la variabile luogo ( estratta al punto 1 ) risulta essere uguale al campo luogo dell'oggetto contenuto in l2 , allora mi fermo ( questo perchè l'elemento risulta giàinserito in l2 )
punto in cui aggiorno il contatore del totale:
3) se la posizione in cui si trova il cursore in l2 è < della lunghezza ( ? )
allora rimuovo la variabile cnt
la incremento
e la reinserisco in coda, questa volta settata. D
Dalla teoria ( se ho capito bene ) non posso modificare direttamente il contenuto di una lista quindi : mi posiziono nel punto da modificare con il cursore ( di solito con moveToPos ) , rimuovo, modifico , e reinserisco.
4) diversamente inserisco un oggetto di tipo conteggio ( luogo , 1 ) perchè imposto di base iltotale ad 1. in quanto non avendo trovato doppioni il contatore di totale non va incrementato.
 
Sì, grossomodo mi sembra che tu abbia capito bene. Permettimi di rispiegartelo a modo mio così provo a chiarirti tutto. Concettualmente l'algoritmo è semplicissimo: per ogni elemento nella lista degli item controllo se ho già un contatore per il suo luogo; se non ce l'ho ne creo uno nuovo (con totale=1, perché ho trovato il primo elemento con quel luogo) e lo inserisco nella lista dei contatori, altrimenti lo rimuovo dalla lista dei contatori, incremento il suo valore (totale++) e lo reinserisco nella lista dei contatori.

C++:
LList<conteggio> L2;                                                  // creo una lista di contatori (inizialmente vuota)
for (L1.moveToStart(); L1.currPos() < L1.length(); L1.next()) {       // per ogni elemento in L1
    // Prendo il luogo dell'elemento attuale
    string luogo = L1.getValue().getluogo();                            

    // Cerco il luogo nella lista dei contatori
    for (L2.moveToStart() ; L2.currPos() < L2.length(); L2.next()) {  // scorro la lista L2
        if (luogo == L2.getValue().getluogo()) break;                 // mi fermo se trovo una corrispondenza sul luogo
    }

    // A questo punto L2.currPos():
    // 1) se l'elemento non esiste è uguale a L2.length() perché ho finito di scorrere la lista;
    // 2) se l'elemento esiste, indica la posizione del contatore relativo al luogo che stiamo esaminando.
    // Gestisco questi due casi nel seguente if-else

    if (L2.currPos() < L2.length()) {
        // Se la posizione corrente su L2 è inferiore alla lunghezza della lista, sono nel caso 2;
        // ovvero, ho trovato il luogo all'interno della lista dei contatori
        conteggio cnt = L2.remove();                                  // rimuovo l'elemento dalla lista dei contatori
        cnt.incrementa();                                             // incremento il suo valore
        L2.append(cnt);                                               // lo reinserisco nella lista dei contatori
    } else {
        // Se la posizione corrente su L2 NON è inferiore alla lunghezza della lista, sono nel caso 1;
        // ovvero, ho finito di scorrere la lista dei contatori e non ho trovato il luogo che stavo cercando
        L2.append(conteggio(luogo, 1));                               // inserisco un nuovo contatore
    }
}

3) se la posizione in cui si trova il cursore in l2 è < della lunghezza ( ? )
Se la posizione in cui si trova il cursore è inferiore alla lunghezza della lista, allora il for innestato si è fermato grazie al break: ho trovato un elemento con uogo == L2.getValue().getluogo() e quell'elemento è l'elemento in L2.currPos(). L'alternativa è che ho raggiunto fine lista, ovvero L2.currPos() == L2.length(), senza trovare il luogo che stavo cercando.

Dalla teoria ( se ho capito bene ) non posso modificare direttamente il contenuto di una lista quindi : mi posiziono nel punto da modificare con il cursore ( di solito con moveToPos ) , rimuovo, modifico , e reinserisco.
In teoria puoi, ti basta implementare quella funzionalità. In pratica non ti è permesso modificare la classe LList e quindi devi ripiegare su questa strategia di togliere e reinserire, invece che modificare. Non c'è bisogno di usare moveToPos perché il for innestato (con il break) già sposta il cursore al punto desiderato.