TDIU04 Programmering i C++, standardbibliotek Vt1 2015
Övningsuppgifter Innehåll Lektion 1 ..................................................................... 2 Lektion 2 ...................................................................... 3 Lektion 3 ..................................................................... 4 Lektion 4 ..................................................................... 5 Lektion 5...................................................................... 6
De tre senaste tentorna finns tillgängliga via kursens examinationssida på webben. Dessa är naturligtvis utmärkta uppgifter att öva på under hemarbetstid. Några laborationsuppgifter innehåller tips om övningar som kan göras som tillägg till eller modifieringar av laborationsuppgifterna. Eftersträva alltid att använda standardbibliotekskomponenter (containrar, algoritmer, iteratorer, funktionsobjekt) för att lösa uppgifter – använd inte handskrivna repetitioner (for, while, do) om det finns passande standardalgoritmer.
1 (6)
Övningsuppgifter för lektion 1 Lektion 1 ska vara förberedande för främst laborationsuppgift 1. Lämplig förberedelse för denna lektion och laboration är att läsa igenom kapitel 12 och Appendix C i C++ direkt samt häftena C++ Algoritmer, en översikt och C++ Containrar, en översikt. 1.
Skriv ett program som upprepat gör följande: • Läser in en rad i taget från cin till en fältvariabel av typ char[]. En rad kan antas ha en viss maximal längd, som dock enkelt ska kunna ändras i koden. Programmet ska vara skrivet så att det får avbryts med Control-C. • Använder algoritmen std::reverse för att vända på innehållet i fältet. • Skriver ut innehållet i fältet på terminalen. • Kopierar innehållet i fältet till en std::string, tecken för tecken, med std::copy (vilket är det enklaste sättet att göra detta?). • Vänder på innehållet i strängen med std::reverse. • Skriver ut strängen på terminalen (nu ska den rad som matades in skrivas ut). Körexempel (användarens inmatning med fet stil): hej hopp ppoh jeh hej hopp i spenaten netaneps i i spenaten ...
2.
Skriv ett program som gör nedanstående. Deklarera iteratorer med uttryckligt list-iteratortyp (välj lämpligast av de fyra i varje situation), använd medlemsfunktioner för att erhålla iteratorer. • Läs in heltal från cin och lagrar dem i en std::list. Om ett tal inte finns i listan sedan tidigare ska talet sättas det in sist, i annat fall görs inget (nästa tal läses in). Avbryts när filslut nås. • Skriv ut listan på cout, använd for-sats och iteratorer. • Sortera listan och skriv sedan ut listans på cout med std::copy och std::ostream_iterator. • Gör en kopia av listan och addera sedan 1 till varje värde i kopian. Använd for-sats och iterator för att stega igenom listan. Skriv ut listan på cout med std::copy och std::ostream_iterator. • Sortera in värdena i kopian i den första listan (vad händer med kopian?), skriv ut listan på cout med std::copy och std::ostream_iterator. • Ta bort alla dubbletter, skriv sedan ut listan på cout med std::copy och std::ostream_iterator. Byt de iteratorbaserade for-satserna som använts mot intervallbaserade for-satser (”range based”). Deklarera saker med auto, om möjligt, och använda ”range access”-funktioner för att erhålla iteratorer. Hemuppgift om tidsbrist.
3.
Skriv ett program som med hjälp av strömiteratorer och algoritmen std::copy läser strängar från en textfil och lagra dem i en std::vector, sorterar strängarna och skriver ut dem på en annan textfil (en sträng per rad). Filernas namn ska anges på kommandoraden. Visa andra (enklare) sätt att läsa in indata till vectorn.
2
Övningsuppgifter för lektion 2 Lektionen är en direkt förberedelse till laboration 2, del 1. Se laborationshandledningen för laboration 2 för mer detaljer om uppgiften! I deluppgift 5, 6 och 7 ska med vanliga styrsatser (while, vanlig for (inte intervallstyrd; range based, do, if). Använd indexering för att komma åt Word_Entry-elementen i vectorn och tecken i strängar. Programmet ska från början läsa indata via cin (indata från fil läses då genom omdirigering på kommandoraden). I deluppgift 8 ska programmet ändras så ett filnamn i stället ges på kommandoraden då programmet startas. 1.
Definiera en struct Word_Entry som kan lagra ett ord, std::string, och en ordräknare, unsigned int (unsigned). Word_Entry ska vara en enkel struct som uppfyller kraven för aggregat.
2.
Använd typedef för att deklarerar namnet Word_List att vara en std::vector med element av typen Word_Entry. Detta är ett äldre, mindre lättläst, alternativ till aliasdeklaration, som denna typedef ska ändras till i del 2 i Laboration 2. typedef namn typdeklaration;
3.
Deklarera följande funktioner, definitioner ska skrivas senare. Välj parametertyper med omsorg. lower_case(sträng) ska göra om alla stora bokstäver i sträng till små. Parametern sträng ska vara inparameter och funktionen ska returnera resultatet. insert(ord, ordlista) ska sätta in ord i ordlista. Parametern ord ska vara inparameter, ordlista ska vara både in- och utparameter. Funktionen ska inte returnera något. print(ordlista) ska skriva ut ordlista. Parametern ordlista ska vara inparameter. Funktionen ska inte returnera något.
4.
Skriv ett program i main() som läser ett ord i taget från cin, gör om stora bokstäver till små, och sedan anropar funktionen insert() för att sätter in ordet i en ordlista. När alla ord har lästs in ska ordlistan skrivas ut.
5.
Definiera funktionen lower_case(ord).
6.
Definiera funktionen print(ordlista). Utskriften ska bestå av två kolumner. I den vänstra kolumnen ska orden skrivas ut högerjusterade i ett fält om 20 positioner. I den högra kolumnen ska respektive ordräknare skrivas ut, vänsterjusterad. Kolumnerna ska separeras med två mellanrumstecken.
7.
Definiera funktionen insert(ord, ordlista). Först ska funktionen kontrollera om ordet redan finns i ordlistan. Om så är fallet ska räknaren för det ordet stegas upp ett steg. Om ordet inte finns ska ett nytt Word_Entry för ordet sättas in sist i ordlistan, med ordräknaren satt till 1.
8.
Ändra programmet så att indata läses från en textfil via en infilström (ifstream). Namnet för indatafilen ska ges på kommandoraden när programmet startas. Programmet ska kontrollera att korrekt antal kommandoradsargument ges och att det går att öppna filen med det angivna namnet. I annat fall ska motsvarande felmeddelanden skrivas ut och programmet avslutas.
När ovanstående övningsuppgifter är lösta har du underlag för att koda laboration 2, del 1. Om inte alla uppgifter hinns med på lektionen, slutför själv hemma innan motsvarande laborationstillfälle.
3
Övningsuppgifter för lektion 3 Lektion 3 ska vara förberedande för främst laborationsuppgift 3 och 4. 1.
Skriv ett program som läser in heltal och lagrar dem i en std::priority_queue, tillsammans med ett ordningsnummer som anger vilket tal i inläsningsordningen det var (först inläst tal erhåller ordningsnumret 1, andra inläst tal ordningsnumret 2, o.s.v.) Ett tal och dess ordningsnummer ska lagras i form av std::pair. När alla tal har lästs in ska innehållet i prioritetskön skrivas ut. Skriv varje tal och dess ordningsnummer på en egen rad, separerade med ett kolon:
2.
Modifiera programmet i uppgift 1 på följande sätt. • Använd typedef eller aliasdeklaration för att ge enkla namn åt dels pair för två int, dels en priority_queue som ska lagra sådana par. • Överlagra operator<< för std::pair, så att de två värdena i ett pair skrivs ut på en utström på formen: first: second
3.
Skriv ett program som läser heltal och skriver ut dem på följande sätt. En körning kan se ut som följande, där användarens inmatning markeras med fet stil, programmets utskrift med vanlig stil: Mata in heltal, avsluta med punkt (.): 1 2 3 1 2 3 4 5 4 5 x FFF Felaktig indata: x Felaktig indata: FFF 6 7 8. 6 7 8 Slut.
Först matas 1 2 3 in följt av Return/Enter, programmet skriver ut 1 2 3. Sedan matas 4 5 in och Return/Enter, programmet skriver ut 4 5, osv. Därefter matas, felaktigt, x FFF in, programmet detekterar detta och hanterar dessa två felaktiga värden. Slutligen matas 6 7 8. in, 6 7 8 skrivs ut och punkten avlutar programkörningen. Detta ska göras genom att undantag kastas av strömoperationer om felaktiga indata matas in, fångas och hanteras, varefter programmet ska kunna fortsätta att läsa indata (otillåtna indata måste tas bort dessförinnan). En punkt som sitter som sista tecken i något felaktigt ska inta avsluta programmet, utan anses tillhöra det felaktigt inmatade.
4
Övningsuppgifter för lektion 4 Lektion 4 ska vara förberedande för laborationsuppgift 5 och efterföljande. 1.
Skriv ett program som läser in heltal och lagrar dem i en std::map, där de inlästa talen utgör nycklarna i mappen (det ena heltalsvärdet) och det andra heltalsvärdet används för att räkna hur många gånger varje tal förekommit i indata. När alla tal har lästs in ska programmet skriva ut varje tal som förekommit i indata och hur många gånger det förekommit. Utskriften kan t.ex. se ut enligt följande (talet 1 förekom 4 gånger, talet 2 förekom 5 gånger, talet 3 förekom 3 gånger, o.s.v.): 1: 2: 3: 4: 5: 6: 7: 8: 9:
2.
4 5 3 4 5 3 2 3 6
Överlagra operator<< som en funktionsmall för att skriva ut godtyckliga objekt av typen std::pair på formen: first: second
operator<< förutsätts finnas för typerna för first och second. 3.
Skriv ett program som läser två textfiler och skriver ut de ord som förekommer i båda filerna. Idén är att man läser in orden i två respektive std::set och tar fram de ord som är gemensamma genom att skapa snittmängden (görs med algoritmen std::set_intersection). De gemensamma orden ska sparas i ett tredje std::set, innan de skrivs ut. Filernas namn ska anges på kommandoraden.
5
Övningsuppgifter för lektion 5 Lektion 5 ska vara förberedande för laborationsuppgift 6. Använd containrar, algoritmer, funktionsobjekt, funktioner, etc., för att lösa uppgifterna! 1.
Läs in heltal (int) till en std::vector. • Sök det första paret av intilliggande (eng. adjacent) lika element. Om det finns två sådana element ska deras värde och deras positioner i vectorn skrivas ut. • Sök det första paret av intilliggande element där det andra är dubbelt så stort som det första. Om det finns två sådana element ska värdet och de två positionerna i vectorn skrivas ut. Definiera en funktionsobjektsklass (struct) twice_over för att hitta två sådana element. Nästlade typer för argumenttyp och returtyp ska finnas (typedef eller aliasdeklarationer). Gör sedan följande modifieringar, en i taget: • Gör om funktionsobjektsklassen twice_ over till en mall (template), så att den kan användas för att alla typer T som uppfyller de krav som implementeringen kräver. • Ersätt twice_over med ett lambdauttryck.
2.
Skapa en std::list med initial storlek 10, för att lagra kan lagra heltal (int). • Fyll på med 10 udda värden. 1, 3, 5, …, 19, genom att använda algoritmen generate och en egen, vanlig funktion odd_by_twos() som genererar de udda värdena (1 vid det första anropet, 3 vid det andra anropet, osv.). • Beräkna summan av alla elementen i listan multiplicerade med sig själva parvis (innerprodukten – det finns en algoritm för detta) och skriv ut resultatet. Gör motsvarande modifieringar som för uppgift 1 (funktionsobjekt, lambdauttryck).
3.
Initiera ett fält a (”array”) med 10 heltalsvärden (int): 0, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9. • Skriv ut värdena i a med copy() och ”range access”-funktionerna begin(a) och end(a); dessa definieras i . • Kopiera värdena i a till en vector, med villkoret att alla jämna värden ska ersättas med noll i kopian. Använd algoritmen replace_copy_if och ett lambdauttryck för att bestämma vilka objekt som är jämna. • Räkna hur många nollor som finns i kopian och skriva ut det antalet. Använd algoritmen count_if och ett lambdauttryck för att bestämma vilka objekt som är noll. • Dags för en lite svårare uppgift! Gör om punkt två ovan, men använd enbart standardkomponenter för att bestämma om ett värde är jämnt – tips: modulus (motsvarar den binära heltalsdivisionsoperatorn%), bind, function och not1. • Gör om punkt tre ovan, men använd enbart standardkomponenter för att bestämma om ett värde är noll. Inte så svårt…
6