Ohjelmointi++ 2002/ 20.3

1. välikoe


Jokainen tehtävä omalle konseptille. Yhdeltä konseptilta arvostellaan vain YKSI tehtävä. Pöytätestille on oma lomake. Tarvittaessa liitä pöytätestiinkin mukaan konsepti. Aikaa 4 tuntia!

Vastaa 4 tehtävään (yht. max 24 pistettä) ja halutessasi Bonus-tehtävään. Jos vastaat useampaan, arvostellaan 4 huonointa. Jos ohjelmatehtävissä käytät standardifunktioita tai metodeja, muttet muista tarkkaan funktion tai metodin nimeä, kirjoita "kuvittelemasi" esittely funktiolle tai metodille.

Muista vastata jokaisen alakohdan jokaiseen kysymykseen niissä tehtävissä, joihin päätät vastata.

1. Pöytätesti

Mitä seuraava ohjelma tulostaa ja mitä ovat muuttujien arvot ohjelman loppuessa? Tutki PÖYTÄTESTIN avulla! (6p)
//--------------------------------------------------------------------
/* vkseko02.cpp - Vuoden 2002 välikokeen pöytätestiohjelma
*/
#include <iostream>
#include <string>
/* 06 */ using std::cout;
/* 07 */ using std::string;
/* 08 */ 
/* 09 */ class cSorsa {
/* 10 */   int vari;
/* 11 */   string nimi;
/* 12 */ public:
/* 13 */   cSorsa() : nimi("Repe") { vari = 0; }
/* 14 */   cSorsa(string &inimi) {
/* 15 */     inimi = "Aku"; vari = 2;
/* 16 */   }
/* 17 */   ~cSorsa() {
/* 18 */     cout << "Sinne meni: "; tulosta();
/* 19 */   }
/* 20 */   void hyppaa(cSorsa apu) {
/* 21 */     apu.nimi = "Senkin Sorsa";
/* 22 */     vari++;
/* 23 */   }
/* 24 */   void tulosta() {
/* 25 */     vari++;
/* 26 */     cout << nimi << " " << vari << "\n";
/* 27 */   }
/* 28 */ };
/* 29 */ 
/* 30 */ int main(void)
/* 31 */ {
/* 32 */   string nimi = "Väiski";
/* 33 */   cSorsa aku(nimi);
/* 34 */   cout << nimi;
/* 35 */   aku.tulosta();
/* 36 */   {
/* 37 */     cSorsa repe;
/* 38 */     repe.tulosta();
/* 39 */     aku.hyppaa(repe);
/* 40 */     aku.tulosta();
/* 41 */   }
/* 42 */   aku.tulosta();
/* 43 */   return 0;
/* 44 */ }

Tehtävät 2-5 liittyvät liitteen esimerkkiin VaihdaTab.cpp. Tehtävät voi käsittää itsenäisinä, mutta niiden merkityksen voi ymmärtää paremmin tutustumalla ensin liitteen ohjelmaan.

2. Algoritmit ja merkkijonot

Tiedostossa on "tolppa"-merkein (|) toisistaan erotettuja kenttiä. Esimerkiksi:
6|28|6|Miten sanat|1|14.3.2002 12:26:06
9|31|13|Luokan tehtäviin|146,1456|14.3.2002 12:37:41
10|30|19|Top down.  Mikä kirjoitet|3,4|14.3.2002 12:41:53

a)
Kirjoita algoritmi, jolla saadaan selville mikä on kunkin sarakkeen pisin kenttä. Tiedostoahan käydään läpi riveittäin. (Algoritmin pienimpänä "yksikkönä" luennolla käytetyt funktiot/C++:n merkkijonoista löytyvät metodit. Eli ei tarvitse etsiä merkki kerrallaan.)(2p)
Esimerkissä pisimmät: 2,2,2,24,8,18
b)
Kirjoita C++:n merkkijonoja käyttäen (string) funktio tai metodi tutki_rivi, joka tutkii ja vertaa rivin kenttien pituuksia tähänastisiin ennätyksiin ja "korjaa" ennätyksiä tarvittaessa. Luennon erota-funktiota saa käyttää. Samoin luokkaa cTabs. Jos tarvitset vielä jotakin muuta funktiota tai metodia (ks. liite), niin kirjoita siitä esittelyrivi (prototyyppi) ja kommentoi mitä funktio tekee. (4p)


3. Tiedostot

Täydennä liitteen VaihdaTab-ohjelman metodit tutki, tulosta_rivi ja tulosta.(6p)

Bonus tehtävä: Voiko ohjelmaa käyttää filtterinä jos sitä muuttaisi siten, että se lukisi std-inputtia nykyisen kiinteän rivit.txt:n asemasta (oletetaan syöte äärelliseksi). Filtteri on ohjelma, jolle voidaan suunnata syöte ja jonka syöte voidaan suunnata taas tiedostoon. Jos voi, niin perustelu, jos ei voi, niin mitä pitäisi muuttaa (ei koodia, idea vaan)?(+2p)


4. C++ ja dynaamisuus

Olkoon luokkaesittely (... tarkoittaa "paljon" koodia) ja pääohjelma:

const MAX_TABS = 5;

class cTabs { unsigned int tabs[MAX_TABS]; public: cTabs(int deftab=0) { ... alustaa taulukon täyteen deftab-arvoa } void lisaa(int i,const string &sana) { ... laittaa paikkaan i sanan pituuden mikäli sana pitempi kuin paikassa i oleva pituus } unsigned int tab(int i) const { ... palauttaa paikassa i olevan pituuden } };

int main(void) { cTabs tabs(8); // => taulukossa arvot 8, 8, 8, 8, 8 tabs.lisaa(2,"kissa istuu"); // => 8, 8,11, 8, 8 tabs.lisaa(1,"kana kotkottaa"); // => 8,14,11, 8, 8 tabs.lisaa(2,"kissa syö hiirtä"); // => 8,14,16, 8, 8 tabs.lisaa(1,"kissa"); // => 8,14,16, 8, 8 tabs.lisaa(9,"kukko kiekuu"); // => 8,14,16, 8, 8 tabs.lisaa(-9,"negatiivinen"); // => 8,14,16, 8, 8 return 0; }



a)
Piirrä kuva tietorakenteesta.(0.5p)
b)
Täydennä luokan muodostaja (constructor) toimivaksi.(1p)
c) Täydennä lisaa-metodi toimivaksi.(1.5p)
d)
Tarvitseeko luokka hajotinta (destructor)? Perustelu!(1p)
e)
Luokassa on se vika, että sen koko voidaan määrätä vain vakiota MAX_TABS muuttamalla. Muuta luokkaa siten, että edellinen toiminta saadaan pääohjelman esittelyllä
  cTabs tabs(8,5);   // => taulukossa arvot  8, 8, 8, 8, 8
missä jälkimmäinen parametri määrää taulukon koon. Nyt koko ei saa riippua mistään muusta kuin tuosta parametrista. Kirjoita kaikki luokan tarvittavat metodit uudelleen niin, että muuten pääohjelman toiminnallisuus säilyy. Toteuta myös tab-metodi. Piirrä kuva tietorakenteesta(3p)
(Taulukon koon suurentamista esittelyn jälkeen ei tarvitse toteuttaa, vaikka hyvä taulukko suurenisikin tarpeen vaatiessa.)

5. Luokat ja yhteistyö

a)
Ohjelma tulostaa vielä "rumasti" numerot. Muuta koodia niin, että puhtaat numerosarakkeet tulostuvat oikeaan reunaan ja muut sarakkeet vasempaan reunaan. Kun rivejä tutkittaan, niin tiedoston ensimmäinen rivi ei vaikuta numeerisuuteen, sillä se on otsikkorivi. Ohjelman VaihdaTab.cpp mallitiedosto tulostuisi muutoksen jälkeen:
 ID tot cor QName                     Answer   Time
  6  28   6 Miten sanat               1        14.3.2002 12:26:06
  7  31  21 Tarvitaan luokat          23       14.3.2002 12:29:11
  8  26  10 Luokan tehtäviin          35       14.3.2002 12:33:45
  9  31  13 Luokan tehtäviin          146,1456 14.3.2002 12:37:41
 10  30  19 Top down.  Mikä kirjoitet 3,4      14.3.2002 12:41:53

Vinkkejä:
// Seuraava on tosi, jos sanan yksikin merkki on muu kuin
// luettelon merkki.  Sallituissa palauttaa -1 jos kaikki
// merkit ovat joukon merkkejä.  Jos jokin sanan kirjain
// ei ole joukossa, palautetaan väärän kirjaimen indeksi.
if ( sallituissa(sana,"0123456789-.") >= 0 ) ...

// Tulostus oikeaan tai vasempaan reunaan voidaan valita: if ( ??? ) os << right; else os << left;


Mitä metodeja muutat ja miten jotta numerot tulostuisivat "kauniisti". (4p)

b)
Mitä seuraavassa muodostajassa tapahtuu ja miksi? Missä merkkijono tiedosto on alustettu? Mikäli tehtävän 4e) dynaamisuus toteutettaisiin luokaan cTabs , niin mitä pitäisi muuttaa luokan cVaihda muodostajassa?(2p)
class cVaihda {
  cTabs tabs;
  char erotin;
  string tiedosto;
...
public:
  cVaihda(char ierotin,int minlev) :
     tabs(minlev), erotin(ierotin) { }
};



Liitteitä

Ote string:in helpistä. size_type on positiivinen kokonaisluku, basic_string on kantaluokka stringille. charT on merkkityyppi (joka stringin tapauksessa on char).
size_type
find (const basic_string& str, size_type pos = 0) const;

Searches for the first occurrence of the substring specified by str in this string, starting at position pos. If found, it returns the index of the first character of the matching substring. If not found, returns npos. Equality is defined by traits::eq().

size_type
find (const charT* s, size_type pos, size_type n) const;
size_type
find (const charT* s, size_type pos = 0) const;
size_type
find (charT c, size_type pos = 0) const;

Searches for the first sequence of characters in this string that match a specified string. The variations of this function return, respectively:

find(basic_string(s,n), pos)
find(basic_string(s), pos)
find(basic_string(1, c), pos)

size_type
find_first_of(const basic_string& str,
size_type pos = 0) const;

Searches for the first occurrence at or after position pos of any element of str in this string. If found, the index of this matching character is returned. If not found, npos is returned. Equality is defined by traits::eq().

Non-member Functions

template <class Stream, class charT, class traits,
class Allocator>
basic_istream<charT, traits>
getline(basic_istream<charT, traits> is,
<charT, traits allocator> str, charT delim);

An unformatted input function that extracts characters from is into str until npos - 1 characters are read, the end of the input sequence is reached, or the character read is delim. The characters are read using traits::char_in().


Palanen mjonotpp.h:n kommenteista:

**    erota                  - laittaa merkkijonon kahtia valitun merkin
**                             kohdalta
**    erota                  - laittaa merkkijonon kahtia ensimmäisen
**                             valitun merkin kohdalta

ja koodista: #define vstring std::string

//------------------------------------------------------------------------------ inline vstring erota(vstring &jono, char merkki=' ',bool etsi_takaperin=false) // Erottaa jonosta valitun merkin kohdalta alkuosan ja loppuosan. // Alkuosa palautetaan funktion nimessä ja loppuosa jätetään // jonoon. Merkin etsimissuunta voidana valita (oletuksena alusta päin). // Jos merkkiä ei löydy, palautetaan koko jono ja tyhjennetään jono. // Käyttöesimerkki: olkoon aluksi string jono,s; // 1) jono = "123 456"; s = erota(jono); => jono == "456" s == "123" // 2) jono = "123"; s = erota(jono); => jono == "" s == "123" // 3) jono = "1 2 3"; // while ( jono != "" ) cout << erota(jono) << ","; => tulostaa 1,2,3, // { size_t p; if ( !etsi_takaperin ) p = jono.find(merkki); else p = jono.rfind(merkki); vstring alku = jono.substr(0,p); if ( p == vstring::npos ) jono = ""; else jono.erase(0,p+1); return alku; }

//------------------------------------------------------------------------------ vstring erota(vstring &jono, const vstring &merkit,bool etsi_takaperin=false) // Erottaa jonosta valittujen merkkien kohdalta alkuosan ja loppuosan. // Alkuosa palautetaan funktion nimessä ja loppuosa jätetään // jonoon. Merkin etsimissuunta voidana valita (oletuksena alusta päin). // Jos merkkiä ei löydy, palautetaan koko jono ja tyhjennetään jono. // Käyttöesimerkki: olkoon aluksi string jono,s; // 1) jono = "123 456"; s = erota(jono," "); => jono == "456" s == "123" // 2) jono = "123"; s = erota(jono,";"); => jono == "" s == "123" // 3) jono = "1;2 3"; s = erota(jono," ;"); => jono == "2 3" s == "1" // { size_t p; if ( !etsi_takaperin ) p = jono.find_first_of(merkit); else p = jono.find_last_of(merkit); vstring alku = jono.substr(0,p); if ( p == vstring::npos ) jono = ""; else jono.erase(0,p+1); return alku; }