Esercizio - Ordinare le parole di un file con sort e lambda (primo appello 2021-22, programmazione)
Prova di programmazione (90 minuti, su carta) del primo appello 2021-22 (25 gennaio 2022) del corso di Laboratorio di Programmazione, Ing. Informatica UniPD. Teoria: STL - algoritmi, predicati e lambdaLa STL ha circa 80 algoritmi generici (header
Testo. Creare un programma che legge tutto il contenuto di un file di testo inserendo le singole parole in un contenitore standard appropriato. Successivamente, ordinare tale contenitore sfruttando l'algoritmo
std::sort(), usando come criterio il numero di lettere di ciascuna parola e, a parità di numero di lettere, l'ordine alfabetico. Implementare tale criterio usando un function object o una lambda expression.
Scelte
Contenitore:
std::vector<std::string>. Serve accesso casuale (richiesto dasort: iteratori random access, chestd::listnon ha) e si aggiungono elementi in coda conpush_back. Unastd::maposetordinerebbero da sole, ma per chiave e senza duplicati: qui le parole ripetute si tengono e il criterio è personalizzato.Lettura:
is >> westrae una "parola" alla volta, saltando spazi e a capo, e restituisce un valore falso alla fine del flusso:for (string w; is >> w; ) parole.push_back(w);. La funzione prende unistream&, quindi funziona con un file (ifstream), concino con una stringa in memoria.Criterio: funzione di confronto strettamente "minore":
aviene prima dibse ha meno lettere, oppure se ne ha lo stesso numero ea < b(ordine lessicografico frastring).È un ordine totale stretto, come richiede
sort.Lambda: scritta direttamente come terzo argomento di
sort; non cattura nulla ([]) e prende le stringhe perconst&per non copiarle.
Programma
#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// Legge tutte le parole (separate da spazi) da un flusso. Si lavora su istream&:
// vale per un file, per cin e per una stringa in memoria.
vector<string> leggi_parole(istream& is)
{
vector<string> parole;
for (string w; is >> w; ) // >> si ferma agli spazi e restituisce falso alla fine del file
parole.push_back(w);
return parole;
}
int main(int argc, char* argv[])
{
vector<string> parole;
if (argc > 1) {
ifstream file(argv[1]); // il nome del file si passa dalla riga di comando
if (!file) {
cerr << "impossibile aprire " << argv[1] << '\n';
return 1;
}
parole = leggi_parole(file);
}
else
parole = leggi_parole(cin); // senza argomenti, legge da tastiera / da pipe
// criterio: prima il numero di lettere, a parita' ordine alfabetico
sort(parole.begin(), parole.end(),
[](const string& a, const string& b) {
if (a.size() != b.size()) return a.size() < b.size();
return a < b;
});
for (const string& w : parole) cout << w << '\n';
return 0;
}Con il file (o l'ingresso standard)
il gatto nero mangia la mela rossa e il cane beve
acqua con la zampasi ottiene (una parola per riga):
e
il
il
la
la
con
beve
cane
mela
nero
acqua
gatto
rossa
zampa
mangiaNell'uscita: e (1 lettera); poi il, il, la, la (2 lettere, alfabetico); poi con (3); beve, cane, mela, nero (4); acqua, gatto, rossa, zampa (5); mangia (6). Le parole ripetute (il, la) restano entrambe.
Il file si passa come argomento della riga di comando (./prog testo.txt); senza argomenti il programma legge da cin. Nell'ambiente di prova in WebAssembly non c'è <fstream>: l'esecuzione è avvenuta leggendo dall'ingresso standard, mentre il ramo con ifstream è stato solo compilato (per Linux).
Versione con function object
Lo stesso criterio come classe con operator():
struct PiuCorta {
bool operator()(const std::string& a, const std::string& b) const
{
if (a.size() != b.size()) return a.size() < b.size();
return a < b;
}
};
// sort(parole.begin(), parole.end(), PiuCorta());La lambda è preferibile quando il criterio serve in un solo punto: resta accanto alla chiamata; il function object è più adatto se va riusato.
Versione ripasso
- Contenitore:
vector<string>(iteratori random access persort,push_back). Lettura:for (string w; is >> w; ) parole.push_back(w);in una funzione suistream&(file,cin, stringa). File dalla riga di comando:ifstream file(argv[1]); if (!file) .... - Criterio: oppure ( e ) (confronto lessicografico delle
string).
sort(parole.begin(), parole.end(),
[](const string& a, const string& b) {
if (a.size() != b.size()) return a.size() < b.size();
return a < b;
});- Function object equivalente:
struct PiuCorta { bool operator()(const string& a, const string& b) const { ... } };esort(..., PiuCorta()). - Prova con
il gatto nero mangia la mela rossa e il cane beve / acqua con la zampa:e,il,il,la,la,con,beve,cane,mela,nero,acqua,gatto,rossa,zampa,mangia. - Errori tipici:
std::listconsort(begin, end)(non random access); confronto con<=(non è un ordine stretto); dimenticareconst&nella lambda; usaregetlinee ottenere intere righe invece delle parole; non controllare l'apertura del file.