Ohjelmointi 1, tentti 16.5.2014. Tentaattori Antti-Jussi Lakanen. Arviointiraportti Yleistä ======= [Tentti](http://users.jyu.fi/~anlakane/ohjelmointi1/tentit/2014-05-16-tentti2.pdf) () oli vaikeudeltaan keskitasoa. Demopisteet lasketaan vielä tuohon päälle arvosanaa laskettaessa. Tehtäväkohtaiset pistekeskiarvot löytyvät alta kunkin tehtävän kohdalta. Omasta tehtäväpaperista saa kopion saa Antti-Jussilta, huone C414.2. Jos en ole paikalla, laita sähköpostia tai soita 0500 603111. Papereihin on merkitty virheet, jotka voin käydä kanssasi läpi, tarvittaessa tarkastajan kanssa. Ensimmäinen uusinta on 16.5.2014, ja toinen uusinta 13.6.2014. | Tehtävä | Teki | Keskiarvo | Tarkastaja | | ------: | ---: | --------: | ------------------- | | T1 | X | X | Antti-Jussi Lakanen | | T2 | X | X | Antti-Jussi Lakanen | | T3 | X | X | Antti-Jussi Lakanen | | T4 | X | X | Antti-Jussi Lakanen | | T5 | X | X | Antti-Jussi Lakanen | | Yht | - | X | | # Tehtävä 1 - Teki: x opiskelijaa - Keskiarvo: x.x pistettä - Palautteet: mittaa x.x, vaikea x.x, arvio x.x, aika x min - Tarkastaja: Antti-Jussi Lakanen ## Malliratkaisu ```c // Tehdään kokonaislukutaulukko, jossa viisi alkiota: // 5, 11, -4, 0, 1 // Huomaa, että viimeinen luku on 1 koska 5/3 on int-lukuna 1. int[] luvut = { 5, 10 + 1, -4, 0, 5 / 3 }; // Tehdään totuusarvoja sisältävä lista, johon laitetaan // "valmiiksi" kaksi alkiota: true ja false List totuudet = new List() { true, false }; // Lisätään totuudet-listaan true, koska // luvut[0] on 5 // luvut[1] on 11 ja 11/2 on 5, joten 5 == 5 totuudet.Add(luvut[0] == (luvut[1] / 2)); // Lisätään totuudet-listaan false, koska // totuudet.Count on 3 ja luvut.Length on 5, ja // niinpä 3 == 5 palauttaa false totuudet.Add(totuudet.Count == luvut.Length); // Järjestetään luvut-taulukko. // Taulukko nyt {-4, 0, 1, 5, 11} Array.Sort(luvut); // Lisätään totuudet-listaan true, koska // luvut[2] on 1 ja luvut[0] + luvut[3] on 1 // niinpä 1 != 1 palauttaa false totuudet.Add(luvut[2] != (luvut[0] + luvut[3])); // Tulostetaan True, False, True, False, False // Huomaa, että totuusarvoja tulostettaessa eka kirjain tulee isolla // Pisteiden kannalta ei väliä kirjoittiko tulostuksen pienillä vai // isoilla kirjaimilla. foreach (bool arvo in totuudet) { Console.WriteLine(arvo); } ``` ## Yleiset huomiot Tehtävä meni pääsääntöisesti aika hyvin. Vähän laiskasti tai epämääräisesti oli kommentoitu rivejä, tai ei oltu ihan uskallettu kertoa, että mitä arvoja `bool`-listaan todella ollaan lisäämässä. Mitenkään valtavan ankara en kuitenkaan ollut, jos vaikutelma kokonaisuudesta oli se, että ymmärrettiin mitä oltiin tekemässä. ## Pisteytys ja virheet - Muiden pääohjelman rivien kommentoinnista enintään 4 pistettä yhteensä, mutta tulostuksesta ja sen oikeellisuudesta 2 pistettä. - Piti siis tietää että mitä *arvoja* `bool`-listaan menee, muuten ei täysiä pisteitä tullut. > Lisätään `true`tai `false`, vertailemalla `totuudet`-listan alkioiden > määrää `luvut`-taulukon alkioiden määrään. - Yllä esimerkki kommentista, joka on totta, mutta jossa olisi pitänyt vielä mainita, että *kumpi* arvo listaan todella lisätään. # Tehtävä 2 - Teki: x opiskelijaa - Keskiarvo: x.x pistettä - Palautteet: mittaa x.x, vaikea x.x, arvio x.x, aika x min - Tarkastaja: Antti-Jussi Lakanen ## Malliratkaisu ```c /// @author anlakane /// @version 16.5.2014 /// /// Lasketaan palavien hehkulamppujen määrä, ja tulostetaan /// palavat lamput kun mennään sata kierrosta. /// public class T2 { /// /// Kaksinkertainen silmukka, jolla /// saadaan aikaan sata kierrosta. /// public static void Main() { bool[] lamput = new bool[101]; for (int i = 1; i < lamput.Length; i++) { for (int j = i; j < lamput.Length; j++) { if (j % i == 0) lamput[j] = !lamput[j]; } } int summa = 0; StringBuilder lamputPaalla = new StringBuilder("Sytytettyinä ovat lamput nro: "); String eteen = ""; for (int i = 0; i < lamput.Length; i++) { if (lamput[i]) { summa++; lamputPaalla.Append(eteen + i); eteen = ", "; } } lamputPaalla.Append("."); Console.WriteLine("Sytytettyjä lamppuja yht.: " + summa + " kpl."); Console.WriteLine(lamputPaalla); } } ``` ## Yleiset huomiot Tehtävä osoittautui vaikeaksi, ja tehtävänanto melko monine vaiheineen hieman hankalaksi ymmärtää. ## Pisteytys ja virheet - Olin melko armollinen, vaikka kokonaisuus ei toiminutkaan. Hyvästä yrityksestä sai pisteitä. - Jos oli kuitenkin tehty ihan väärää asiaa, niin siitä ei annettu pisteitä. Tällaisia olivat esimerkiksi seuraavat: silmukassa mentiin läpi väärää asiaa, lamppuja sytytettiin "miten sattuu", päällä olevien lamppujen kokonaismäärä laskettu täysin epäloogisesti, jne. # Tehtävä 3 - Teki: x opiskelijaa - Keskiarvo: x.x pistettä - Palautteet: mittaa x.x, vaikea x.x, arvio x.x, aika x min - Tarkastaja: Antti-Jussi Lakanen ## Malliratkaisu * Kohta 1. `int` on kokonaislukutyyppi, ja `bool` totuusarvoinen tyyppi. Esimerkiksi `int munIka = 31;`, tai `bool peliKaynnissa = false;`. * Kohta 2. Muuttujat nimetään C#:ssa nk. camelCasing-säännön mukaan, siis ensimmäinen kirjain pienellä ja seuraavien sanojen ekat isolla. Ks. esimerkkejä kohdassa 1. Vakiot nimetään suuraakkosin, ja sanojen väliin tulee alaviiva, esimerkiksi `int PALLOJA_ALUKSI = 10;`. * Kohta 3. Esimerkki funktiosta ja sen käytöstä. ```c public static int Summa(int[] luvut) { int summa = 0; foreach(int luku in luvut) { summa += luku; } return summa; } ``` Ja tätä voi kutsua (esimerkiksi pääohjelmasta käsin) ```c int[] lukuja = {4, 5, 6, 1, 0}; int munSumma = Summa(lukuja); ``` * Kohta 4. Vertailuoperaattori palauttaa `true` tai `false`. Esimerkkejä: `==` on yhtäsuuruusoperaattori, jolla voi vertailla esimerkiksi kokonaislukujen tai totuusarvojen yhtäsuuruutta. Vastaavasti `!=` on erisuuruusoperaattori, esimerkiksi `1 != 1` palauttaa `false`, sillä 1 *ei* ole erisuuri kuin 1. Edelleen, `<` on "pienempi kuin" -operaattori, esimerkiksi `5 < 4`, palauttaa `false`. * Kohta 5. Rekursiivisella aliohjelmalla tarkoitetaan sellaista aliohjelmaa, joka tarvitsee itseään ratkaistaakseen ongelman. Rekursiivinen aliohjelma siis kutsuu itse itseään. * Kohta 6. `19.125_10` --> `10011.001_2` ```javascript 19 = 2 * 9, jj 1 | 0.125 * 2 = 0.25 9 = 2 * 4, jj 1 | 0.25 * 2 = 0.5 4 = 2 * 2, jj 0 | 0.5 * 2 = 1 2 = 2 * 1, jj 0 | 1 = 2 * 0, jj 1 | Kokonaisosa 10011 | Desimaaliosa 100 (luetaan alh->ylos)| (luetaan ylh->alas) Siis koko luku 10011.001 ``` ## Yleiset huomiot - Meni pääsääntöisesti tosi hyvin. Ei isompia ongelmia. ## Pisteytys ja virheet - Kohta 1: Piti olla esimerkit tietotyypeistä (esimerkiksi `int`, `bool`, `char`, `double`, jne.) sekä niiden käytöstä (esim `int luku = 0;`, `char kirjain = 'a'`, tms.) Jos ei ollut esimerkkiä niin puoli pistettä pois. - Kohta 3: Funktio palauttaa arvon, joten paluuarvon tyyppinä *ei* voi olla `void` vaan jotain muuta. Tätä eivät kaikki muistaneet. - Kohta 3: Jos funktion palauttamaa arvoa ei kutsun yhteydessä sijoitettu mihinkään, niin siitä pikku vähennys -0.25 p. - Kohta 6: Jos laskutoimituksissa tai tulkinnassa virheitä niin melko armottomasti vähennyksiä tästä. # Tehtävä 4 - Teki: x opiskelijaa - Keskiarvo: x.x pistettä - Palautteet: mittaa x.x, vaikea x.x, arvio x.x, aika x min - Tarkastaja: Antti-Jussi Lakanen ## Malliratkaisu Tulosmatriisi näyttää tältä ``` 1 1 1 1 1 1 1 2 3 4 5 6 1 3 6 10 15 21 1 4 10 20 35 56 1 5 15 35 70 126 1 6 21 105 126 252 ``` Joten reittejä oikeaan alakulmaan on 252 kappaletta, ja jokaiseen soluun vievien reittien määrä saadaan laskettua summaamalla `rivi-1` ja `sarake-1`, missä `rivi` ja `sarake` merkkaavat sitä paikkaa (indeksiä) missä ollaan menossa, kun `rivi` ja `sarake > 0`. Miksi näin? Siksi, että ainoat mahdollisuudet tulla kuhunkin soluun on yläkautta (`rivi-1`) *tai* vasemmalta (`sarake-1`), joten nämä summaamalla saadaan kaikkien mahdollisten reittien lukumäärä. Alla olevassa koodissa on pituuden säästämiseksi jätetty kirjoittamatta `Tayta`-funktio sekä luokka, ja `Main`-pääohjelman dokumentaatio. Koko ohjelma dokumentaatioineen löytyy täältä: ```c public static void Main() { int[,] matriisi = Tayta(6, 6, 1); for (int i = 1; i < matriisi.GetLength(0); i++) { for (int j = 1; j < matriisi.GetLength(1); j++) { matriisi[i, j] = LaskeReittienLukumaara(matriisi, i, j); } } Console.WriteLine(matriisi[5, 5]); } /// /// Funktio palauttaa matriisin tietyn paikan /// (rivi, sarake) reittien lukumäärän, kun solusta toiseen /// "liikkuminen" voi tapahtua ainoastaan oikealle ja alaspäin. /// Esimerkiksi, soluun (1, 1) voi tulla kahta eri reittiä; /// ensin oikealle ja alas, tai sitten ensin alas ja vasta /// sitten oikealle. /// /// Matriisi /// Rivi /// Sarake public static int LaskeReittienLukumaara(int[,] matriisi, int rivi, int sarake) { if (rivi == 0) { return matriisi[rivi, sarake-1]; } if (sarake == 0) { return matriisi[rivi-1 , sarake]; } return matriisi[rivi - 1, sarake] + matriisi[rivi, sarake - 1]; } ``` ## Yleiset huomiot Tässä tehtävässä kävi vähän niin, että joko saatiin selkeästi kiinni, mistä on kysymys, tai sitten ei lainkaan ymmärretty. Tämä teki pisteytyksestä vähän "binääristä", eli joko aika lailla 6 p. tai 0 p. Vaikeuksia oli joillakin saada kiinni siitä, mitä ollaan tekemässä (algoritminen ajattelu). Kuten edellisessä kohdassa sanottiin, oleellista oli huomata, että solun reittien määrä saadaan summaamalla yläsolu + vasen solu. Ohjelmoinnin kannalta tehtävä oli aika helppo, tästä lisää alla. Varsinainen kirjoitettava osa tässä tehtävässä oli `Main`-pääohjelman kaksi sisäkkäistä silmukkaa, jonka sisään tuli `LaskeReittienLukumaara`-funktiokutsu ja arvojen sijoitus sopivasti `matriisi`-muuttujaan. `LaskeReittienLukumaara`-funktio itsessään oli hyvin lyhyt ja simppeli, joten sinänsä tehtävä oli ohjelmointimielessä helppo. Pääohjelman sisäkkäiset silmukat voidaan aloittaa riviltä 1 ja sarakkeesta 1, koska ekalle vaaka- ja pystyriville voidaan todellakin tulla vain yhtä reittiä pitkin, joten niiden reittien lukumäärää ei tarvitse "uudestaan" laskea pääohjelmassa. ## Pisteytys ja virheet - Kuten sanottu, pisteytys meni aika lailla 0 p. tai 6 p. Epäoleellisista pikkuvirheistä ei sakotettu. Dokumentaatiot puuttuivat joltakin, siitä -1 p. # Tehtävä 5 - Teki: x opiskelijaa - Keskiarvo: x.x pistettä - Palautteet: mittaa x.x, vaikea x.x, arvio x.x, aika x min - Tarkastaja: Antti-Jussi Lakanen ## Malliratkaisu ```c using System; using System.Text; using System.Linq; using System.Collections.Generic; /// @author anlakane /// @version 16.5.2014 /// /// Lotto-ohjelma, joka arpoo ja tulostaa 7 /// varsinaista numeroa ja 3 lisänumeroa väliltä 1-39. /// public class T5 { /// /// Arvotaan ja tulostetaan numerot. /// public static void Main() { int[] pallot = new int[39]; // Taulukon paikkaan nro i tulee aina luku i+1 // eli paikkaan 0 arvo 1, paikkaan 1 arvo 2, ..., // paikkaan 38 (viimeinen paikka) luku 39. for (int i = 0; i < pallot.Length; i++) { pallot[i] = i + 1; } Sekoita(pallot); Console.WriteLine("Varsinaiset numerot:"); for (int i = 0; i < 7; i++) { Console.WriteLine(pallot[i]); } Console.WriteLine("Lisänumerot:"); for (int i = 7; i < 10; i++) { Console.WriteLine(pallot[i]); } } /// /// Aliohjelma sekoittaa annetun kokonaislukutaulukon /// alkiot keskenään Fisher-Yates-algoritmilla /// /// Sekoitettava taulukko. public static void Sekoita(int[] luvut) { Random r = new Random(); for (int i = luvut.Length - 1; i > 0; i--) { int j = r.Next(i); int apu = luvut[j]; luvut[j] = luvut[i]; luvut[i] = apu; } } } ``` ## Yleiset huomiot Hieman yllättäen taulukon (tai listan), jossa on lukuja peräkkäin, tekeminen oli yllättävän haastavaa. Näkyi esimerkiksi ehdotus `int[] luvut = new int[1, ..., 39]`, joka on valitettavasti väärin. Pitää muistaa, että hakasulkujen sisään tulee taulukon koko, eikä alkioiden arvoja. Sivujuonteena mainitsen vain, että olisi hirveän kiva jos muoto `int[] luvut = {1..30}` (huomaa aaltosulut) toimisi C#:ssa, tähän tyyliin perättäisten lukujen jono tehdään monissa kielissä, esimerkiksi Haskellissa. Vielä kerran: Jos pitää toistaa useita kertoja jotakin juttua (esimerkiksi laittaa monta lukua tai arvoa taulukkoon), niin silloin tarvitaan toistorakennetta eli silmukoita. Ks. alla. ```c int luvut = new int[39]; // Tehdään taulukko, jonka pituus 39 for(int i = 0; i < luvut.Length; i++) { // Laitetaan kuhunkin taulukon alkioon arvo i + 1 // (tarkempi selitys malliratkaisussa) luvut[i] = i + 1; } ``` Jos ei päästy kiinni siihen, miten luvut saadaan taulukkoon, niin herkästi ne jäi sitten myös sekoittamatta (ja tulostamatta). Eli tässäkin tehtävässä tuppasi olemaan hivenen kaksihuippuinen arvostelujakauma. ## Pisteytys ja virheet - Ei oltu laitettu lukuja 1-39 taulukkoon lainkaan tai oli laitettu väärin, -1 p. - Muut virheet tapauskohtaisest.