Autori risto postitused

PR1ET4: Funktsioonid

Labori materjal

Esitamisele kuuluvad ülesanded

Selles laboris on kokku kolm ülesannet. Kõigil ülesannetel on antud koodipõhi, mis määrab ära ülesande üldise struktuuri ja annab sulle ettemääratud töövoo. Töövoog on antud samm-sammuliste juhenditena, alguses täpsemalt, lõpupoole üldisemalt.

Ülesandeid 2 ja 3 on võimalik laiendada lisaülesannetega.

Ülesanne 1: Elektri hinna kalkulaator

Antud ülesandes on sinu eest juba kogu programmi loogika valmis tehtud. St kogu main()  funktsiooni sisu on juba valmis ja seda muuta ei ole põhjust.

Tegemata on jäetud aga enda loodavate (user-defined) funktsioonide sisud ning need on vastavalt asendatud kõik tagastama 0 väärtust (selleks, et programm kompileeruks). Sinu ülesandeks täita kõigi funktsioonide deklaratiivne osa (keha) vastavalt funktsioonile eelnevale kommentaarile.

Lae alla ülesande aluskood; https://blue.pri.ee/ttu/files/iax0583/aluskoodid/4_1_electricity_template.c

Nõuded

  • Ülesande lahendamist alusta baaskoodiga tutvumisest. Sinu lahendus peab olema ehitatud ette antud baaskoodile.
  • Sinu ülesandeks on kirjutada funktsioonide kehad seitsmele funktsioonile. Funktsiooni eesmärk on kirjeldatud selle ees kommentaarina.
  • main()  funktsiooni sisu ja koodi struktuuri muuta ei tohi. Soovitav on vältida ka funktsioonide ja muutujate ümbernimetamist.

Vihjeid ja soovitusi

  • Alusta tutvumist koodi ülesehitusest. Vaata kus on makrod, kus prototüübid, kus main() ja kus funktsioonid. Vaata kuidas main() funktsioonist teisi funktsioone välja kutsutakse, mis kaasa antakse jne. Ära muuda midagi alguses!
  • Üldine pilt selge, hakka järjest funktsioonide sisusid täitma.  Lahenda 1 funktsioon korraga, kompileeri ja testi enne kui järgmise juurde lähed!
  • Sisestuse funktsioonides on soovitatav struktuur do while tsükkel, mille sees on if lause veateate väljastamiseks.
  • Kõik ülejäänud funktsioonid on lahendatavad ühe reaga, asendades olemasoleva return 0  asemel õige valemi
  • Ettevaatust ühikutega. Erinevates kohtades on kord kasutusel vatt-tunnid, kord kilovatt-tunnid, kord megavatt-tunnid! Juhindu funktsiooni kommentaaridest.
  • VAT – value added tax tähendab käibemaksu (Eestis 20%)
  • Jooksev börsihind MWh kohta (enne makse) on leitav https://dashboard.elering.ee/et
  • Jooksvat börsihinda kWh kohta sentides koos maksudega näitab https://www.elektrikell.ee

Testimine

Test 1: veavaba sisend

Test 2: vigadega sisend

Ülesanne 2: Arvujadade generaator

Selle ülesande raames tuleb koostada programm, mis sisaldab endas kahte arvujada generaatorit, aritmeetilise ja geomeetrilise jada genereerimiseks. Ülesannet laiendab lisaülesanne, mille raames lisatakse algarvude generaator ja muudetakse programmi väljundit kenamaks.

Jälgi ülesande lahendamisel samm-sammulist juhendit.

Lae alla ülesande aluskood: https://blue.pri.ee/ttu/files/iax0583/aluskoodid/4_2_sequence_gen_template.c

Nõuded

  • Funktsioonide lahendamisel juhindu funktsiooni kommentaarist ja siin lehel olevast samm-sammulisest juhendist.
  • Programm peab suutma genereerida aritmeetilist ja geomeetrilist jada vastavalt etteantud sisenditele.
  • Kasutaja peab saama valida kumba generaatorit kasutada ning sisestada sellele parameetrid (algväärtus, vahe või tegur ja sammude arv).
  • Jada algväärtus, geomeetrilise jada tegur ja aritmeetilise jada tegur sisestatakse kümnendmurruna.
  • Jada liikmed kuva kahe komakohaga.

Samm-sammuline juhend

  1. Kompileeri aluskood. Proovi seda, vaata mis ta teeb. Loe kood läbi.
  2. Lisa main()  funktsiooni väljakutse funktsioonile  PrintMenu() . Väljakutse asukoht on märgistatud TODO kommentaarina. Kirjuta väljakutse kommentaarist järgmisele reale. Proovi – kui töötab, kustuta TODO kommentaar ära.
    KOMPILEERI JA TESTI, ET TEHTU TÖÖTAB ENNE EDASI LIIKUMIST!
  3. Kirjuta funktsiooni  PrintSeparator()  sisu. Selleks pead kirjutama tsükli,  mis trükib   #  sümbolit  täpselt nii mitu korda  kui oli ette antud funktsiooni parameetriga.
    KOMPILEERI JA TESTI, ET TEHTU TÖÖTAB ENNE EDASI LIIKUMIST!
  4. Lõpeta funktsioon  PrintAsciiWelcomeMsg()  , pannes see väljastama endale meelpärast ASCII kunsti (pilt, tekst, …). Võid näiteks kasutada ka pildi teisendajaid ASCII kunstiks või kasutada mõnda teost internetis olevatest galeriidest.
    KOMPILEERI JA TESTI, ET TEHTU TÖÖTAB ENNE EDASI LIIKUMIST!
    Vihje: Internetis on mitmeid ASCII galeriisid ja teisendajaid. Üks nest on kättesaadav siit: https://www.asciiart.eu
    NB! Osad tähemärgid omavad erilist tähendust ja neid ei saa niisama lihtsalt ekraanile manada. Sellisteks on näiteks  \  (escape sequence) ja  %  (formaat ehk format specifiers). Nende väljastamiseks peame  \  asemel kirjutama  \\  ja  %  asemel  %%. Kõige lihtsam viis (kui selliseid sümboleid on palju) on teha Geanys uus fail, panna oma kunstiteos sinna ja kasutada kogu dokumendi ulatuses asendust (kiirklahv ctrl+h avab menüü, valik replace all in document).
  5. Nüüd mine ArithmeticSequence()  funktsiooni juurde.
    1. Deklareeri puuduvad muutujad (3 tk kokku, ettevaatust andmetüüpidega!)
    2. Lisa kasutajale päringud ja loe väärtused loodud muutujatesse.
    3. Lisa funktsiooni  ArithmeticSequenceGenerator()  väljakutse koos vajaminevate parameetritega.
      NB! Märka, et funktsioon ArithmeticSequenceGenerator()  on koodist välja kommenteeritud! Seda tegime selleks, et vähendada liigseid kompilaatori hoiatusi mis võivad summutada muud olulised hoiatused ja veateated sinu jaoks. Kuniks sa seda funktsiooni sisse ei kommenteeri, annab kompilaator sulle  “undefined reference” veateate ja programm ei kompileeru. Kommenteeri see funktsioon sisse, et saaksid testimise läbi viia (hetkel veel tulemusteta)!
      KOMPILEERI JA TESTI, ET TEHTU TÖÖTAB ENNE EDASI LIIKUMIST!
  6. Liigu edasi funktsiooni ArithmeticSequenceGenerator()  juurde ja lõpeta see. Sul on vaja kirjutada üks tsükkel, mille sisse tuleb paigutada 2 lauset
    1. Väljasta praeguse jada liikme väärtus.
    2. Arvuta järgmise jada liikme väärtus.
      NB! Järjekord on oluline!
      Vihje 1: järgmine_väärtus = praegune_väärtus + vahe Vihje 2: Sul ei ole tegelikult vaja siin funktsioonis ühtegi uut muutujat deklareerida peale tsükliloenduri. . Kindlast väldi massiivide kasutamist (teema järgnevateks nädalateks).
      KOMPILEERI JA TESTI, ET TEHTU TÖÖTAB ENNE EDASI LIIKUMIST!
  7. Menüüs, mis on realiseeritud  switch ()  lausena, on valmis kirjutatud vaid üks case . Lisa veel 2 puuduolevat menüü valikut ja nendele vajaminevad laused. Vajaminevad käsud on kirjeldatud menüüs ( PrintMenu()  funktsioonis)
  8. Samal viisil nagu lahendasid aritmeetilise jada genereerimise funktsioonid tuleb nüüd lahendada ka geomeetrilise jada genereerimiseks vajaminevad funktsioonid.

Nüüd peaks ülesanne lahendatud olema. võrdle oma väljundit etteantud testjuhtudega!

Testimine

Testi kindlasti kõiki kolme menüü valikut ning ka tundmatut sisendit!

Testime aritmeetilist jada

Testime geomeetrilist jada

Lahkume ilma jada genereerimata

Ülesanne 3: Vastuvõtuaegade planeerimine

Selles üleandes lood lihtsa generaatori millega saab luua ajavahemike vastuvõtuaegade planeerimiseks

Lae alla aluskood: https://blue.pri.ee/ttu/files/iax0583/aluskoodid/4_3_timetable_template.c

Soovitus: Enne ülesande lahendamist loe läbi ja mõtesta enda jaoks  lahti etteantud algoritm (pärast nõudeid)! Kui tekib segadus, soovitan piiluda sisse lähenemise peatükki.

Nõuded

  • Tööpäeva algus on määratud makrona
  • Kasutaja sisestab klientide arvu ning ühe kliendikohtumise kestvuse
  • Programm genereerib kliendikohtumiste kellaajad. Ajad on järjestikused.
  • Minimaalselt peavad olema realiseeritud ja kasutatud aluskoodis antud funktsioonid. Funktsioone võid juurde luua.
  • Genereeritud ajad peavad olema reaalsed
  • Väljund peab olema visuaalselt joondus
  • Aluskoodis on kõigi funktsioonide kohta on antud vaid nende nimed. Sa pead määrama nende tagastatava andmetüübi ja parameetrid. Kui parameetrid puuduvad, siis tuleb sulgudesse kirjutada void .

Algoritm

Lähenemine

Sel korral on lähtekood juba üsna õhuke, kuid sisaldab siiski struktuuri kuidas oleks mõistlik see programm funktsioonideks tükeldada.

Iga funktsiooni puhul mida lahendad, alusta selle tagastuse andmetüübi ja parameetrite loetelu määramisest. Need on sulle ette antud funktsiooni kommentaarina. Kui see on tehtud, lisa funktsiooni prototüüp enne main() funktsiooni. Seejärel kirjuta funktsiooni kena ning lisa funktsiooni väljakutse sobilikku kohta.

Ava mind vihjete nägemiseks
  • Esmalt alusta asjadest, mis on juba koodis kasutusel ja/või poolikult realiseeritud. Sellisteks funktsioonideks on  PrintTime()  ja PrintTimeInterval() . Need peaksid olema üsna lihtsad realiseerida ja saad mõned häirivad hoiatused eest ära.
  • Nüüd realiseeri GetPositiveInt()  funktsioon. Selle funktsiooni oled juba eelnevate tunnitööde raames teinud. Määra tagastuse tüüp ja funktsiooni parameetrid ning kopeeri eelnevast ülesandest kood sisse. Seejärel saad funktsiooni kasutada nii klientide arvu kui kliendikohtumise kestvuse lugemiseks.
  • Järgmisena tuleks alustada tööd funktsiooni  PrintTimetable()  kallal. Kutsu see funktsioon main() -ist välja. Funktsiooni sisse loo tsükkel klientide ja nende kellaaegade väljastamiseks. Ära kellaaegade peale veel mõtle, veendu lihtsalt, et kliendide loetelu kuvataks korrektselt.
  • Kellaaegade saamiseks tuleb lahendada funktsioonid  CalcNextHour()  ja CalcNextMin() . Mõlemad funktsioonid on lahendatavad ühe reaga, pead vaid vaid piisavalt täpse matemaatilise tehte välja mõtlema ja tulemuse tagastama.
    Vihje: Sa saad nende funktsioonide töötamist testida ka eraldiseisvalt ülejäänud koodist. Näiteks kutsudes funktsioon välja
    CalcNextHour(8, 0, 50)  peab tagastatavaks väärtuseks olema 8 ning CalcNextHour(8, 0, 70)  puhul 9. Sarnaselt peab teise funktsiooni korral CalcNextmin(8, 0, 50)  tagastama 50 ja  CalcNextmin(8, 0, 70)  korral 10.
  • Nüüd mine tagasi funktsiooni   PrintTimetable()  ja hakka sinna kirjutatud tsüklit sisustama. Vaata kindlasti algoritmi võrdluseks.
    NB! Funktsiooni korrektseks tööks on sul vaja teada nii kohtumise alguskellaaega (märgitud kui hetkeaeg – current time) kui lõpukellaaega. Kui need 4 väärtust on teada, kasuta neid funktsiooni PrintTimeInterval()  väljakutses.
  • Nüüd on aeg testimiseks ja vajadusel silumiseks. Kõik jupid koodist on valmis. Veendu, et need töötavad ootuspäraselt.

Testimine

Testime vaid minuti ületäitumisega

Testime tundide ületäitumist

Testime kõiki ületäitumisi korraga ja mitmekordselt

Lisaülesanded

Selles laboris on kaks lisaülesannet, vastavalt teisele ja kolmandale baasülesandele.

Lisaülesanne 1: Algarvude generaator ja rea pikkuse piiramine

Tegu on laiendusega baasülesandele.

Nõuded

  • Kasuta lahendatud laboriülesannet selle töö põhjaks
  • Lisa algarvude generaator. Kasuta järgmises punktis etteantud funktsiooni enda lahenduse koostamisel.
  • Lisa limiit mitu tulemust ühe rea kohta generaatorid kuvavad. Leia enda programmile sobilik limiit.
    Alternatiivina võid teha programmi kenamaks, piirates hoopiski väljastatavat tähemärkide arvu. Selleks tuleb loendada väljastatavaid tähemärke. Üks võimalikest abistatavatest funktsioonidest võiks olla snprintf() .

Funktsioon algarvude tuvastamiseks

Kasuta järgnevat funktsiooni testimaks kas tegu on algarvuga või mitte. NB! Märka, et pead lisama enda koodi ka IS_PRIME makro!

Vihje: Funktsiooni tagastust saad kontrollida otse if  lause tingimuses.  https://blue.pri.ee/ttu/coding-guides/conditional-statements/#Function_calls_in_conditionals

Testimine

NB! Märka, et sel korral on näidatud kahte erinevat lahendust. Mõlemad on korrektsed ja aktsepteeritavad lahendused sellele ülesandele. Sa pead näitama neist vaid ühe.

Teine ülesanne on antud inspiratsiooniks kui soovid väheke rohkem pead murda ja teha väljund veelgi kenamaks.

Test: Väljundis piiratud tulemuste arv

Test: väljundis piiratud tähemärkide arv

Lisaülesanne 2: Lisa puhkepausid ja mitmele päevale ulatuv planeerimine

Tegu on laiendusega baasülesandele.

Nõuded

  • Kasuta lahendatud laboriülesannet selle töö põhjaks
  • Lisa kasutaja poolt seadistatav paus iga kliendikohtumise vahele (nt 10 minutit).
  • Lisa tööpäeva lõpu kellaaeg. Kui kliendikohtumine ületaks seda aega, siis planeeri kohtumine hoopis järgmisse päeva. Kui kohtumiste plaan osutub mitmepäevaseks, näita iga päeva ees ka päeva numbrit.

Testimine

Näide oodatavast väljundist kui broneering läheb mitmepäevaseks koos pausidega.

Pärased tundi peaksid

  • Teadma lihtsamaid polsterdamise ja joondamise võimalusi
    • Täisarvude polsterdamine nullide või tühikutega
    • Kümnendmurdude polsterdamine
    • Sõnede polsterdamine ja joondamine
    • Liigpikkade sõnelõppude ära lõikamine
  • Teadma mis erinevus on lokaal- ja globaalmuutujal
  • Teadma mis on funktsioon ja mis on nende olulisus programmeerimisel
  • Saama aru, et tegelikult kasutasime juba esimesest nädalast peale funktsioone.
  • Mõistma järgmisi mõisteid
    • Funktsiooni prototüüp
    • Funktsiooni tagastuse tüüp
    • Funktsiooni argumendid
    • Funktsiooni parameetrid
    • Funktsiooni päis
    • Funktsiooni keha
  • Oskama ise koostada funktsioone (kasutaja-defineeritud funktsioonid)
  • Oskama väärtusi (konstandid, makrod, muutujate kaudu) edastada funktsioonidesse
  • Oskama salvestada funktsioonist tagastatavat väärtust
  • Oskama oma koodi tükeldada funktsioonideks
  • Omama väikest kogust universaalseid funktsioone, mida saad kasutada tulevikus järgnevate programmide koostamisel! See loetelu hakkab iga nädalaga laienema.

Täiendav materjal

pretest_23

Task

Your task is to generate a F1 racing event results. The results will be displayed on the screen with an option to store them to a file.

Generic requirements

  • Program must be written in either C90, C99 or C11 standard. GNU extensions are permitted. The necessary C standard version must be specified in the header of the code file.
  • You can only use standard libraries and the GNU extensions.
  • The program must compile under either
    • Ubuntu 22.04 with GCC-12 (based on the recommended software)
    • OpenSUSE Linux 15 SP4 with GCC-11 (lab comfiguration)
  • All arrays must be created with static sizes
    • Arrays must be large enough that the program can operate within the specified limitations. Specification is listed under task specifications
    • Program does not need to work with larger inputs, but also must not crash when those are entered. The decision on how to handle improper input is left to the developer.
  • If the program uses dynamic memory allocation, all of the best practices regarding it must be followed. All dynamically allocated memory must be freed before exit. Note that dynamic memory is not a part of Programming 1, so it is not necessary to use it.
  • Program must be divided into functions.
  • Use of global variables is forbidden
  • Use of GOTO statements is forbidden
  • The user experience for the program must be clear. Any errors  that occur must be described to the user in a clear manner. The program is not allowed to “just close” without informing the user what happened.

Program flow general description

  1. The program will read the names of the pilots from an input file and pick the participating pilots.
  2. The number of laps will be specified by the user inside of the program by inputting the number..
  3. All lap times are generated as random numbers.
  4. Total time is calculated for each pilot who participated.
  5. Pilots, their lap times and the total time is shown on the screen as a table.
  6. If an output file name was specified as a command line argument, all results are also written to the output file with that name as a CSV file.

Task specific requirements

  • The program will take either 1 or 2 arguments from the command line
    • Execution pattern:   ./binary_name number_of_pilots [name_of_output_file]
    • First argument is the number of pilots participating. This argument is mandatory.
    • Second argument is the name of the output file. This argument is optional. If this argument is given, an output file that name with the name will be created. Results will be stored into that file.
  • The program must support up to 10 participating pilots. The maximum length for the name name of a participant is 32 characters. The number of laps can range from 5 – 15.
  • The first n pilots will be chosen to participate in the race, based on the order that they are given in the input file. n is the number of pilots, given from the command line. You must validate that you have enough pilots given in the data file.
  • Each lap time will be a randomly generated number between 70 and 125 seconds.
  • Each pilot will have a 3% probability that they have to abandon the race during any lap. If a pilot needs to abandon, they will no longer drive the following laps. The decision (dice roll) will be redone on each lap.
  • For pilots who abandon the race, a -  symbol will be used for the lap that they stopped on and all the following laps. Previous lap times must be present. The total time for that pilot will be presented as DNF (did not finish).

Data file requirements

Input file

The names of pilots will be read by the program from a file called pilots.dat.  The data file must be in the same directory as the binary fine. Name of the file will be hardcoded into the program.

The data file is formatted as an ASCII text file, containing all pilots with the format  initial.last_name.  Names are separated by a space. The number of names can range from 5 – 10.

Names for the input file

Output file

The program will generate a CSV file. File must contain a header with the column names. As data, you need to include the name, all lap times and total time. The output file will be created in the same directory as the binary file.

Examples of expected output

Example  1

Example 2:

Example 3:

 

PR2ET14: SQL

Labori materjal

Laboriülesanded

Selle labori käigus liidestame me enda C-keelse programmi SQLite3 andmebaasiga kasutades libsqlite3-dev  teeki. Esimese ülesande käigus alustad andmete lisamisest olemasolevasse andmebaasi, millele järgneb andmete pärimine teise ülesande raames.

Laboril on kaks edasijõudnute ülesannet, millest esimeses muudame andmete lisamist paremaks. Teises ülesandes tuleb uuendad juba andmebaasis olevaid andmeid.

Lae alla tunnitööks kasutatav andmebaas: study_information.db

Andmemudel

Ülesanne 1: Lisa iseennast andmebaasi

NB! Ülesanne võib osutuda mitte tehtavaks kui andmebaasi fail asetseb P kettal tänu Windows SMBle. Andmebaas jääb lukustatuks ning muudatuste lisamine ei tööta! Lahenduseks on töö ajaks andmebaasifail hoiustada  väljaspool P ketast, nt töölaual ning hiljem sinna kopeerida.

Selle ülesande raames harjutad INSERT päringuid andmete lisamiseks. Baasülesanne ei ole vajalik realiseerida C koodis. Kirjuta oma päringud ja jooksuta neid kasutades “Execute SQL” funktsiooni SQLite browser rakenduses.

Nõuded
  • Kirjuta üleskõik SQL päringud mida sa jooksutad (nt tavalise tekstidokumendina)! Näita jooksutatud päringuid ning näita oma andmebaasi pärast päringute tegemist. Kokku peab olema 5 päringut!
  • Tabeli primaarvõtmeks olevaid ID väärtusi (students.id , subjects.id , declarations.id ) ei tohi lisamispäringutesse sisse kirjutada! Need teeb andmebaas ise (id atribuut on andmebaasi disainis omadusega auto increment ning kõigi kolme tabeli id atribuudid omavad vastavat generaatorit)
  • Teosta järgmised andmete lisamise päringud
    • Lisa andmebaasi enda kui tudengi andmed
    • Lisa andmebaasi õppeaine, mille oled sooritanud
    • Lisa endale 3 aine deklaratsiooni. Üks nendest peab olema ainele, mille just lõid, lisaks 2 tk olemasolevatele.
  • Märkus: hinded ja isikukood ei pea olema reaalsed.

Kui andmed lisatud, näita enda tehtud päringud ning näita oma andmebaasi SQLite Browser rakenduse kaudu.

Ülesanne 2: Andmete pärimine

Selle ülesande raames tuleb sul kirjutada päringuid millega andmebaasis eksisteerivaid andmeid pärida. Kõik päringud peavad olema kirjutatud C-keelse programmi sisse. Sulle on antud 3 koodinäidet selle lehe alguses materjalide all. Vali milline neist on sulle kõige arusadavam ning kasuta seda mallina enda päringute kirjutamisel.

Ülesanne on jaotatud kolmeks eraldi hinnatavaks osaks.

Nõuded
  • Kõik päringud vastavas ülesande osas peavad olema sooritatud. Iga osa hinnatakse eraldi.
  • Andmed peavad olema täielikult ette valmistatud andmebaasihalduri poolel – st kõik arvutused (nt summa, keskmine, max jne) peavad olema kirjutatud SQL päringu sisse. Arvutusi C koodis ei tohi teha!
  • Kõik olulised andmeväljad tuleb väljastada. Ära väljasta identifikaatori väärtusi (nt subjects.id, declarations.id)
  • Päringuid võid jooksutada läbi menüü valikuliselt või ühes rakenduses üksteise järel. Sinu otsustada.
  • Kõik ressursid peavad olema programmi lõpuks vabastatud – kontrolli lekete puudumist valgrindiga.
Ülesanne 2 hinnatav osa 1

Esimeses osas teeme lihtsaid SELECT päringuid

Päring 1: Leia kõik õppeained, mis on vähem kui 6 EAPd

Päring 2: Leia kõik õppeaineid, milles on eksam. Järjesta tulemused EAPde arvu alusel vähimast suurimani.

Ülesanne 2 hinnatav osa 2

Teises osas harjutame veidi keerukamaid SELECT päringuid, kus tuleb kasutada JOIN märksõna ühe- või kahekordselt, et liita mitme tabeli vahelisi andmeid kasutades nende PK-FK võtmepaare.

Päring 1: Leia õppuri ‘Marko’ kõik hinded

Päring 2: Leia kõik õppeaineid mis sa oled sooritanud. Näita neid koos hinnetega, alustades kõrgeimast hindest.

Ülesanne 2 hinnatav osa 3

Kolmandas osas harjutame andmete koondamist tunnuste alusel ja tulemuste arvutamist oma SELECT päringutes. Meil on vaja nii JOIN kui GROUP BY märksõnu ning mõningaid arvutamise funktsioone.

Päring: Leia iga tudengi poolt teenitud EAPde arv ning keskmine hinne.

Edasijõudnute ülesanne 1: Enda andmebaasi lisamine (usaldusväärsemalt)

Esimese baasülesande raames ei olnud sul otseselt piiranguid kuidas ennast andmebaasi lisama peaks. Võisid vaadata mis ID väärtused tulid ning neid siis jooksvalt kasutada. Reaalses elus kasutatava andmebaasi puhul päris nii mõistlik teha ei oleks.

Taustinfot

Paremaks lahenduseks pakun välja 2 võimalust. Kummagi puhul pole tegu ideaalse lahendusega, kuid saame sammu võrra paremuse poole.

Võimalus 1a: SQL toetab alampäringuid – me saame ühe päringu sisse peita teise päringu ja selle tulemust siis kasutada esimeses päringus. Näiteks me saame INSERT päringu sisse kirjutada SELECT päringu.

Oletame, et tahame oma näidisandmebaasi lisada isikule Marko Mets veel ühte autot. Selleks saaksime jooksutada järgmise päringu.

Antud näide pole veatu – näiteks kui meil oleks 2 isikut kelle nimi on Marko Mets, siis läheks see katki. Sellest parem versioon on 1b, mille võid esitada laboriülesandena.

Võimalus 1b: Sarnaselt eelmise võimalusega saad teha sarnase päringu eesti isikukoodi põhjal. Tegu ei ole garanteeritult eksisteeriva väärtuse ega ei pruugi ka unikaalsust garanteerida kui välisriigi isikukoodid sekka tuua aga on parem kui eelmine.

Võimalus 2: Parem viis sellele läheneda oleks teha mitu päringut. Esiteks lisaksime end tudengina andmebaasi. Seejärel päriksime andmebaasilt mis meie ID väärtuseks sai kasutades  SELECT last_insert_rowid();  päringut. See annab meile tagasi automaatselt genereeritud ID väärtuse. Seejärel saaksime juba kasutada seda ID väärtust järgnevate andmete lisamiseks

Ka see pole veatu ega ideaalne. Näiteks kui meie kahe päringu vahel lisatakse mõne teise klientrakenduse poolt uus õppur, siis saaksime tagasi vale koodi (race condition). Seda lahendatakse tegelikus maailmas mehhanismiga TRANSACTION, mis paneb baasi lukku päringute ajaks teistele – ehk siis kõik või mitte midagi meetod. Küll aga kuna meil on kohalik failipõhine andmebaas, siis me saame sellest täna mööda vaadata.

Nõuded
  • Kirjuta enda andmebaasi lisamise päringud C keelse rakenduse sisse.
  • Kui lisamise koodi jooksutatakse mitu korda siis tuleb veenduda, et duplikaate ei tekiks (kontrolli kasutades mõnda teadatuntud unikaalset väärtust – nt kas sinu eID juba eksisteerib).
  • Pead kasutama automaatselt genereeritud ID väärtusi. Ühtegi ID väärtust ei tohi koodi jäigalt sisse kodeerida.
    • Kasuta ühte eelnevalt välja pakutud meetoditest.

Edasijõudnute ülesanne 2 : Olemasolevate andmete muutmine

Andmete muutmiseks andmebaasis saame kasutada UPDATE päringuid. Selliste päringute jaoks peame esmalt tuvastama millist rida me uuendada soovime ja seejärel saame kirjutada sisse uuendatud väärtused. Oluline on, et tuvastaksime ainult selle või need read, mida me peameuuendama. Ülesande raames pead genereerima kõigile tudengitele Uni-ID tunnused.

Nõuded
  • Genereeri kõigile tudengitele Uni-ID tunnused
  • Uni-IDd tuleb lisada andmebaasi kasutades UPDATE lauset. Täita tuleb uni_id atribuut.
  • Kood peab töötama ükskõik kui paljude tudengite puhul
  • Kui genereeritav Uni-ID juba eksisteerib, pead tegema konfliktilahendust. Selle jaoks peaksid kirjutama vastava koodi C keeles.

Pärast seda tundi peaksid

Täiendav materjal

PR2ET13: Puud

Labori materjal

Laboriülesanded

Selle labori baasülesande käigus pead looma kaks programmi ning võrdlema nende jõudlust. Edasijõudnute ülesande raames tuleb luua kolmas programm ning võrrelda selle jõudlust eelneva kahega.

Laboriülesanne: lineaarotsing vs kahendotsingpuu

Labori baasülesande raames on sul tarvis luua kaks programmi. Mõlemad täidavad täpselt sama ülesannet, leides mitu korda iga nimi kordub ette antud andmefailis. Esimese programmi raames pead kasutame lineaarset otsingut koos dünaamilise massiiviga unikaalsete kirjete hoidmiseks. Teises programmis täidab sama rolli meile kahendotsingpuu.

Läbi labor jälgides samm-sammulist juhendit.

Nõuded
  • Leia kõik failis olevad nimekombinatsioonid (ees + perenimi).
  • Loenda mitu korda iga nimekombinatsioon esines.
  • Väljasta tulemused väljundfaili
    • NB! Kuna ülesande eesmärk on saavutada paremat jõudlust, võid puu lahenduse juures kasutada globaalset failiviita. Ära ava korduvalt oma väljundfaili!
    • Kasuta järgnevat väljundi struktuuri:
      <eesnimi> <perenimi> <korduste arv>
  • Loo kaks lahendust samale ülesandele
    • Esimeses lahenduses kasuta dünaamilist massiivi, mida laiendad jooksvalt realloc() funktsiooniga.
    • Teises lahenduses kasuta binaarotsingpuud.
  • Arvuta kui kaua kulus sinu programmil andmete töötlemiseks aega
    • Pead arvestama vähemalt faili lugemise ja andmestruktuuri loomise ajakuluga
    • Võid arvesse võtta ka faili kirjutamise ja vabastamise
    • Tulemused peavad olema võrreldavad! St kui üks programmidest arvestas aja sisse vabastamise, siis peab ka teine seda tegema.
  • Kuva rakenduses mitu unikaalset kombinatsiooni rakendus leidis
    • NB! Kuna rakendus peab olema
  • Print the number of unique combinations your program found
    • NB! Kuna ülesande eesmärk on saavutada paremat jõudlust, võid puu lahenduse juures kasutada globaalset loendurit.
  • Juhindu ülesande lahendamisel ajakulu optimeerimisele. Jäta struktuuri liikmeks olevad sõned staatiliseks – nt char combinedName[64]
Programm 1:Lineaarotsing

Alustuseks ava näiteprogramm  example_linear.c  ning tutvu selle ülesehituse ja sisuga. Proovi seda käivitada ja vaata väljundit.  See saab olema su aluskood.

Märka, et programm kasutab käsurea argumente – st käivita seda järgnevalt:  ./program_name input_file_name

Tutvus tehtud, alustame koodi muutmist enda ülesandele sobivaks.

  1. Muuda lugemisfunktsioon sobilikuks ülesandele
    • Praegune versioon faili lugemisest eeldab ühte nime (ühesõnaline). Tunnitöö andmefailis on 4 välja rea kohta – eID, first name, last name and city.
    • Muuda fscanf()  funktsiooni, et see saaks hakkama täiendavate väljadega. eID ja linna salvestada vaja pole, ülesanne neid ei vaja.
    • Kleebi nimi kokku kujule  "Eesnimi Perenimi" . Kasuta kleebitud kuju nii otsingufunktsioonis veendumaks kas tegu on uue nimega või mitte ning uue nime puhul lisa see oma andmestruktuuri.
      Vihje: sprintf() on kõige lihtsam viis nime kokku kleepida.
  2. Loenda mitu korda nimi esinenud on
    1. Lisa oma struktuuri kirjeldusse loendur – kasuta kas  int või  unsigned andmetüüpi.
    2. Enne kui hakkad oma loendamise osa kirjutama, vaata kuidas näitekoodis olev unikaalsuse kontroll ja laiendamine on ehitatud.
      Vihje: Vaata mida tagastab  CheckNameUnique()  funktsioon. Funktsiooni tagastus on võimekam kui selle praegune kasutusjuht näitekoodis.
    3. Lisa oma loendurile algväärtustamine olukorras kus said teada, et nägid nime esmakordselt (nimi ei ole massiivis).
    4. Lisa oma loenduri väärtuse uuendamine (suurendamine) olukorras, kui nimi oli korduv (juba lisatud andmebaasi).
  3. Muuda oma väljastuse funktsiooni
    1. Muuda väljastust sedasi, et tulemus kirjutataks faili
    2. Väljasta nii nimi kui korduste arv
  4. Väljasta mitu unikaalset nime kokku failist leiti
  5. Lisa andmete vabastamine
  6. Lisa aja mõõtmine
    1. Lisa 2 clock  tüüpi muutujat
    2. Pane paika 2 ajavõtupunkti – koht kust alustad aja mõõtmist (enne faili lugemist) ja koht kus lõpetad (nt enne väljastuse alustamist või pärast  mälu vabastamist)
    3. Arvuta palju aega kulus. Väljasta ajakulu.
  7. Jooksuta koodi läbi valgrindi ja veendu selle korrektsuses. Nt. valgrind ./example_linear random_people_city_data.txt
Programm 2: Kahendotsingpuu

Alustuseks ava näiteprogramm  example_bin_search_tree c  ning tutvu selle ülesehituse ja sisuga. Proovi seda käivitada ja vaata väljundit.  See saab olema su aluskood.

Märka, et programm kasutab käsurea argumente – st käivita seda järgnevalt:  ./program_name input_file_name

Tutvus tehtud, alustame koodi muutmist enda ülesandele sobivaks.

  1. Uuenda struktuuri kirjeldust. Sul on vaja hoida seal nii sõne kui loendurit, täpselt nagu lineaarse otsingu puhul.
  2. Muuda lugemisfunktsiooni sarnaselt nagu tegid lineaarse otsinguga.
    1. Muuda fscanf() funktsiooni toetamaks failis olevat nelja parameetrit.
    2. Kleebi ees- ja perenimi kokku.
  3. Uuenda lisamise funktsiooni InsertNode()
    1. Muuda selle parameetrit, et see toetaks lisamisel sõnet (meie puhul siis nimi)
    2. Muuda võrdlemise operatsiooni. Pane tähele, et erinevalt näitekoodist peame me tuvastama ka olukorda, kui puus täpselt sama sõne juba eksisteerib. Sellisel juhul peame loendurit uuendama.
  4. Uuenda uue tipu loomise funktsiooni CreateNode()
    1. Muuda parameetrit, et sinna saaks edastada nime
    2. Lisa loenduri algväärtustamine
  5. Uuenda puu väljastamise funktsiooni PrintTree()
    • Vii sisse uuendused lähtuvalt muudetud andmestruktuurist
    • NB! Kuna meie eesmärgiks on kiire programm, siis võid  sel korral failiviida luua globaalse muutujana. Muidugi kohustust pole, võid vabalt ka selle parameetrina edastada.
    • Muuda puu läbikäiku. Näites on puu läbikäiguks valitud eesjärjestus. Leia selline läbikäigu meetod, mille puhul oleks vastus sorteeritud tähestikulises järjekorras. Läbikäigu muutmiseks pead vahetama rekursiivsete kutsete ja praeguse liikme väljastamise järjekorda.
  6. Realiseeri vabastamise funktsioon
    • Nii nagu kõik puu funktsioonid, peab ka vabastamine olema rekursiivne. Võta inspiratsiooni puu väljastamise funktsioonist. NB! Oluline on valida õige puu läbikäigu meetod (ees, järe või keskjärjestus). Vale meetodi puhul ei ole võimalik puud vabastada.
  7. Lisa aja mõõtmine
  8. Jooksuta koodi läbi valgrindi ja veendu selle korrektsuses. Nt. valgrind ./example_bin_search_tree random_people_city_data.txt
Programmi tööaja võrdlemine

Nüüd võrdle mõlema programmi ajakulu. Kumb on aeglasem, miks?

Mõtle ka kuidas võiks ajakulu muutuda kui

  • Muudaksid realloc()  mälu laiendamise strateegia (n + 1) pealt  (n * 2) peale lineaarotsingul.
  • Unikaalsete nimede arv suureneks märkimisväärselt. Kuidas kumbki rakendus sellele reageeriks – ürita väljendada end Big O  notatsiooni kasutades (https://www.bigocheatsheet.com)
  • Failis oleks märkimisväärselt rohkem nimesid, kuid unikaalsete nimede arv jääks samaks (tõuseks korduste arv)
  • Kui lahendasid ka edasijõudnute ülesande, siis kaasa samasse võrdlusse ka trie andmestruktuur. Kas sellel on ka mõni märkimisväärne eelis tavalise kahendotsingpuu ees?
Testimine

Järgnevad näited sisaldavad väljundit kõigi kolme versiooni tulemustefaili algusest. Võrdle enda loenduse tulemusi allolevatega.

Kokku peab olema  11440 unikaalset nimekombinatsiooni.

NB! Näidetes on ainult rakenduste arvutustulemused, et saaksid neid enda omadega võrrelda. Tegu ei ole programmi väljundiga – programm peab väljastama palju unikaalseid nimekombinatsioone esines ning kui kaua rakenduse töö kestis.

Lineaarne otsing
Kahendotsingpuu ja Trie

Edasijõudnute ülesanne: Trie puu

Loo kolmas programm ning võrdle selle efektiivsust eelneva kahe programmiga. Kasuta kolmandas rakenduses trie andmestruktuuri.

Kõik teised nõuded jäävad samaks.

Soovituslik andmestruktuur

Kasutame muudetud versiooni slaididel olnud näidisest. Asendame struktuuris oleva tõeväärtuse ( isLeaf ), mis näitas sõne lõppu, hoopis täisarvulise väärtusega. Kui väärtus on 0, siis selles asukohas nime lõppu ei ole. Kui väärtus on nullist suurem, siis on tegu lehega, mis tähistab nime. Number ütleb mitu korda nimi failis eksisteeris.

Vihje 1: Lisaks tähestikule arvesta ka tühiku ja miinuse sümboliga. Mõlemad neist on olemas andmefailis. Täiendavaid erisümboleid failis ei ole.

Vihje 2: Andmestruktuuri väljade algväärtustamine CreateNode()  funktsioonis on äärmiselt tähtis. Algväärtusta nii loendur kui kogu viitade massiiv..

Pärast seda tundi peaksid

Täiendav materjal

PR2ET12: Ahelloend

Labori materjal

Laboriülesanded

Selles laboris on üks laboriülesanne, mida laiendab 2 edasijõudnute ülesannet

Laboriülesanne: Ahelloend

Baasülesandes tuleb sul luua rakendus, mis koostab ahelloendi, väljastab selle ning võimaldab loendist otsinguid teostada.

Lae alla aluskood: https://blue.pri.ee/ttu/files/iax0584/aluskoodid/12_ll_basecode.zip

Nõuded
  • Loo menüüprogramm, mis kasutab ühesuunalist ahelloendit
  • Lisatavad elemendid tuleb paigutada loendi algusesse.
  • Iga elemendi sees on täisarv (id) ja sõne (nimi)
    • id on automaatselt genereeritud, unikaalne, järjestikuline täisarv, mis algab nullist.
    • Nimi on sõne, mille sisestab kasutaja uue elemendi loomise käigus
    • Nime jaoks rakenda dünaamilist mälujaotust vastavalt nime sisestatava pikkusele.
  • Ülesande lahendamise käigus tuleb sul luua minimaalselt järgnevad funktsioonid: PrintNode() , CreateNode() , InsertNode() , FindNodeByID() , FindNodeByName()  ja Unload()  .
  • Lahendus tuleb luua etteantud aluskoodi peale.
  • Lahendust luues järgi samm-sammult järgnevat juhendit. Loo funktsioonid vastavalt seal kirjeldatud nõuetele.
Samm-sammuline juhend

NB! Märka, et struktuuri deklareerides on loodud andmetüübile kaks erinevat nime –  struct node  ja list . Mõlemad tähendavad sama asja – st on aliased (nimekaimud) tänu tüübidefinitsioonile. Kasutuse poole pealt on funktsioonid koostatud sedasi, et struct node  viitab ühele elemendile, samal ajal kui list  viitab loendile kui tervikule.


Esimene funktsioon, mis tuleb luua, on ühe ahelloendi elemendi kõigi liikmete väljastamiseks (st id, nimi ja viit järgnevale elemendile). Funktsiooni prototüüp on juba päisefailis olemas. Sinul tuleb luua funktsiooni keha ja paigutada see koodifaili.

PrintNode()  funktsiooni peaksid kasutama iga kord, kui tuleb väljastada ühe elemendi sisu. Sedasi kui loendis olevad liikmed peaksid muutuma tuleb muudatus väljastuseks teha vaid ühes kohas. Ülesande lõpupoole on sul seda funktsiooni vaja ka nt otsingufunktsioonide tulemuste väljastamiseks.

Kontrolli, et funktsiooni poleks edastatud NULL-viita. NULL-viida puhul väljasta veateade, kehtiva viida puhul väljasta elemendi sisu.

Kommenteeri sisse testimise esimene osa (stage 1) ja jooksuta oma programmi!

NB! Testjuhtudes on sõned kirjutatud UTF-8 vormingus, mis töötab väga kenasti Linuxis, kuid võib tekitada veidi lahkhelisid Windowsi keskkonnas. Probleemide korral muuda sõnesid.

Kliki mind, et näha väljundit

NB! See funktsioon on juba sinu eest valmis tehtud. Pööra tähelepanu funktsioonis oleva tsükli ülesehitusele! Sul on seda vaja loendi läbikäiguks  järgnevate funktsioonide lahendamisel.

Selle funktsiooni eesmärgiks on väljastada kõigi ahelloendis olevate elementide sisu. Funktsioonile PrintList()  edastatakse ahelloendi esimese liikme mäluaadress ning tsükli käigus liigutakse läbi ahelloendi kuni jõutakse loendi lõppu, mida tähistab NULL -viit.

Funktsioonile võib edastada ka   NULL -viida. See juhtub tavaliselt siis, kui loend mida väljastatakse osutub tühjaks.

Funktsioon ise väljastust ei teosta, vaid kasutab selle jaoks eelmises etapis loodud  PrintNode()  funktsiooni.

Kommenteeri sisse testimise teine osa (stage 2) ja jooksuta oma programmi!

Kliki mind, et näha väljundit

Seda funktsiooni kasutame uue elemendi loomiseks ja väärtustamiseks. Funktsioon tagastab vastloodud uue elemendi aadressi. Väärtused elemendile antakse kaasa parameetritena.

  1. Loo ja anna mälu uuele elemendile
  2. Määra ära ID väärtus (järjestikuline suurenev täisarv, kasuta  static  märksõna)
  3. Küsi mälu ja kopeeri struktuuri nimi
  4. Väärtusta  pNext  viit
  5. Tagasta struktuur

NB! Siin funktsioonis on kahel korral vaja mälu anda. Mõlemat tagastust tuleb kontrollida! Vea korral tagasta NULL-viit! Programm peab vea korral jätkama töötamist.

Kommenteeri sisse testimise kolmas osa (stage 3) ja jooksuta oma programmi! Ainukesed visuaalselt nähtavad erinevused on aadressiruumi muutus elementidel (läksime funktsioonide pinust kuhja). Valgrindiga käivitades näed mälu küsimiste arvu suurenemist!

Kliki mind, et näha väljundit

Selle funktsiooni eesmärgiks on lisada eelmise funktsiooni poolt loodud uus element  pNode  ahelloendisse, millele viitab ppHead . Topeltviit on vajalik, et lisada uus element loendi algusesse.

Baasülesandes lisame uue elemendi loendi algusesse. Selleks on vaja ainult 2 omistuslauset kirjutada!

NB! Enne lisamist kontrolli, et  pNode  poleks NULL -viit!

Kommenteeri sisse testimise neljas osa (stage 4) ja jooksuta oma programmi! Visuaalselt väljundis midagi ei muutu võrreldes eelmise korraga.

Kliki mind, et näha väljundit

Seda funktsiooni kutsutakse hävitajaks (destructor), mille eesmärk on andmestruktuur mälust vabastada. Vabastamise käigus vabastatakse ka struktuuri liikmed, mis on dünaamiliselt loodud – meie puhul on selleks nimi.

Ahelloendi vabastamiseks on vaja tsüklis käia läbi kogu loend.  Läbikäigu ideega tutvu  PrintList()  funktsiooniss.

Loendi vabastamise juures on oluline pöörata tähelepanu praeguse elemendi vabastamisele. Nimelt pärast praeguse elemendi vabastamist ei ole meil enam ligipääsu selle pNext  viidale. Sellest tingitult on meil vaja vabastamiseks täiendavat abiviita praegusele elemendile, mis teeb selle protseduuri natuke keerukamaks.

Tsüklis on 3 olulist sammu. Kõige elegantsema tsükli saad kasutades while()  tüüpi tsüklit!

  1. Praeguse elemendi aadressist koopia tegemine (seda koopiat kasutame vabastamiseks)
  2. Järgmise elemendi juurde liikumine ( pCurrent = pCurrent->pNext )
  3. Praeguse elemendi ja selle liikmete vabastamine.

Kommenteeri sisse testimise viies osa (stage 5).  Jooksuta programmi kasutades valgrindi! Peaksid nägema, et rakendus on 6 korda mälu küsinud ning vabastanud.

Kliki mind, et näha väljundit

Selles osas loome kaks otsingufunktsiooni, millest üks otsib nime ja teine ID väärtuse järgi. Kui otsing annab tulemust (st loendis on selline element), tagastatakse viit leitud elemendile. Kui elementi loendis ei ole, tagastatakse NULL-viit.

Kasuta  PrintNode()  funktsiooni leitud elemendi väljastamiseks.

Kommenteeri sisse testimise viiesosa (kuues 6) ja jooksuta oma programmi! Märka, et näidisväljundis on veateade väljastatud PrintNode() poolt ja ei ole seotud otsinguga!

Kliki mind, et näha väljundit

Palju õnne! Kõik baasfunktsionaalsuseks vajaminevad funktsioonid on nüüd valmis! Nüüd lisa abifunktsioonid ja koodilaused, et rakendus oleks kasutatav läbi menüü. Testimiseks mõeldud koodi võid soovi korral oma rakendusest kustutada.

Lisada tuleks näiteks nime küsimine uue kirje loomisel või otsitava sisestused otsingufunktsioonide kasutamiseks ning vajadusel tagastuste töötlemiseks. Kui valmis, anna märku tunnitöö esitamiseks!

Testimine

Siin on näha täielikult valmis programmi väljund.

Kliki mind, et näha väljundit

Edasijõudnute ülesanne 1: Tähestikulises järjestikus sorteeritud loend

Selle ülesandega muudame oma elementide lisamise funktsiooni selliseks, et loend oleks alati sorteeritud tähestikulises järjekorras

Nõuded
  • Uuenda InsertNode()  funktsiooni sedasi, et uus element paigutataks olemasolevate vahele sedasi, et loend oleks tähestikulises järjekorras
    • Selleks pead toetama elemendi lisamist loendi algusesse, lõppu või keskele
  • Uuenda   FindNodeByName()  funktsiooni, et see lõikaks kasu tähestikulisest järjekorrast.
  • Kui oled varasemalt lahendanud edasijõudnute ülesande 2, siis uuenda ka eemaldamise funktsiooni, et ka see lõikaks kasu tähestikulisest järjekorrast.
Märkused
  • Vastavalt loendi asukohale kuhu element lisatakse viitasid käidelda erinevalt.
  • Veendu asukohas enne lisamise alustamist!

Edasijõudnute ülesanne 2: Elementide eemaldamine

Loo kaks funktsiooni elementide eemaldamiseks loendist vastavalt nime või ID alusel.

Nõuded
  • Realiseeri  RemoveNodeByName()  funktisoon, mis eemaldab elemendi nimelise vaste korral.
  • Realiseeri RemoveNodeByID()  funktsioon, mis eemaldab elemendi ID vaste korra.
  • Võimalusel kasuta ära omadust, et loend on juba sorteeritud tähestikulises järjekorras (edasijõudnute ülesanne 1)
  • Veendu loendi terviklikkuses pärast elemendi eemaldamist. Testi loendi keskelt, algusest ja lõpust!
Märkused
  • Esmalt leia kas element üldsegi eksisteerib mida peaksid eemaldama
  • Eemaldamisel ühesuunalisest loendist on sul vaja meeles pidada eelmise elemendi asukohta! Seetõttu ei saa sa korduvkasutada  FindNodeBy()  funktsioone! (Saaksid, kui see oleks kahesuunaline ahelloend).
  • Algusest, keskelt ja lõpust eemaldamine on erinev! Kontrolli positsiooni enne eemaldamise alustamist, seejärel vali sobilik eemaldamise meetod.
  • Ära unusta mälu vabastada!
  • Topeltviit on vajalik loendi algusest eemaldamiseks.

Pärast seda tundi peaksid

Täiendav materjal

PR2ET11: Pinu ja rekursioon

Labori materjal

Laboriülesanded

Selles laboris on kaks baasülesannet, üks iseseisev edasijõudnute ülesanne rekursioonile ning üks edasijõudnute laiendusülesanne pinule.

Ülesanne 1: Rekursiooni ülesannete komplekt

Esimese ülesande raames tuleb sul luua kolm rekursiivset programmi (osa 1, 2 ja 3). Ülesanne saab esitada ainult siis, kui kõik kolm on valmis!

NB! Tegu on äärmiselt lihtsate ülesannetega, väldi internetist lahenduste otsimist!

Tunni alguses luuakse näidislahendus faktoriaali rekursiivsest lahendamisest!

Nõuded
  • Võid laheduse luua ühe failina kus on kolm rekursiooni korraga või kolme erineva programmina. Näited on tehtud kolme erineva rakendusena.
  • Kõigil juhtudel on kohustuslik toetada sisendina positiivset täisarvu. Negatiivse sisendi korral on kas lahenduses märgitud eraldi vastus (baasjuht) või tuleb kuvada viga.
  • Sisendi võid küsida rakenduse sees või lugeda käsurea argumendina.
  • Iteratiivseid lahendusi kasutada ei tohi, kõik kolm osa peavad olema lahendatud rekursiivselt.
  • Esitada saab ülesannet vaid siis, kui kõik kolm osa on valmis.
Osa 1: summa

Loo rakendus, mis leiab kõigi positiivsete täisarvude summa kuni kasutaja sisestatud arvuni. Negatiivse argumendi korral rekursiivsele summa funktsioonile on summa 0 (positiivsed arvud puuduvad, baasjuht).

Osa 2: astendamine

Loo rakendus, mis lubab astendada xy. math.h teegi ja sisse ehitatud astmefunktsioonide kasutamine ei ole lubatud.

NB! Kuigi nõue on vaid rakendus luua positiivsetele täisarvudele, siis võid proovida ka laiendada lahendust näiteks negatiivsetele arvudele.

Osa 3: Fibonacci arvud.

Loo rakendus, mis leiab soovitud Fibonacci arvu. n-indaks fibonacci arvuks on selle kahe eelneva Fibonacci arvu summa ehk siis fibx = fibx – 1 + fibx – 2. Erinevalt eelnevatest rekursioonidest on Fibonacci jadal kaks seetõttu ka 2 baasjuhtu.

Fibonacci arvud on  0, 1, 1, 2, 3, 5, 8, 13, 21, …,

Märkasid kui kaua aega läks viimase leidmiseks? See on ootuspärane laboriülesande lahendamisel ning parandada vaja ei ole, kuid mõtle kindlasti miks nii juhtus!

Kõik 3 valmis, näita nüüd ülesanne ette!

Programmi ajakulu saad mõõta kasutades time  programmi. Siin on võrdluseks kaks erinevat rekursiivset lahendust samast programmist. Teisel juhul on kasutatud lähenemist mida kutsutakse tail-recursion’iks ehk sabarekursiooniks. Selle kohta võid vajadusel uurida omal käel.

Ülesanne 2: pinu

Selles ülesandes loome menüüprogrammi, mille sisuks on demonstreerida pinu funktsioone. Tunnis lahendatav ülesanne on demonstratiivne ning toetab varasemalt õpitud dünaamilist mälu – selline lahendus ei too oma keerukuse tõttu välja pinu eeliseid kiiruses!

Tunni alguses luuakse osaline näidis, kus lahendame ära Push() funktsiooni!

Nõuded
  • Loo oma rakendusele menüü struktuur – kasutaja peab saama valida millist operatsiooni teostada
  • Loo Pop() ja Peek() funktsioonid
    • Pop() eemaldab pealmise elemendi pinust ja tagastab selle väärtuse.
    • Peek() tagastab pealmise elemendi, kuid ei eemalda seda
  • Loodavad funktsioonid ise elementide väärtusi kuvada ei tohi! Ekraanile kuvatakse väärtused pärast menüüsse tagastamist!
  • NB! Baasversioonis on lubatud reserveerida üks väärtust tähistamaks pinu alatäitumist, et väljastuses illegaalset väärtust ei kuvataks. Edasijõudnute versioonis see lubatud ei ole.
  • Vastavalt funktsioonile veendu, peavad olema realiseeritud kas ala- või ületäitumise kontrollid.
  • Pööra tähelepanu olukorrale kus pinust eemaldatakse kõik elemendid – reallociga 0 baiti küsida on ohtlik, tuleks kasutada free funktsiooni!
Pinu silumisfunktsioon

Selleks, et saaksid paremat aimu pinu sisust pakun sulle ka silumisfunktsiooni. Tegelikus olukorras sellist funktsiooni sageli rakendada ei saaks kuna see võib olla vastuolus pinu piirangute ning ka konkreetse pinu realisatsiooniga!.

Testimine

Testimisel pööra tähelepanu järgnevale:

  • pinu ületäitumine
  • pinu alatäitumine
  • pinu andmete vabastamine pärast viimase elemendi kustutamist (valgrind!)
  • Mälulekke ohule kui programm suletakse kui pinus on veel andmeid (valgrind!)
Ava mind väljundi nägemiseks

Edasijõudnute ülesanne 1: rekursiooni praktiline kasutusjuht

See edasijõudnute ülesanne on sulle antud ingliskeelse algoritmi kirjeldusena. Tegu on täiesti eraldiseisva ülesandega, mis ei ole seotud eelnevalt tehtud tunnitöö lahendusega!

NB! Olulised muutujad on antud matemaatikutele omases kirjapildis ehk üksikute tähtedena – soovitus oma programmis kasuta pikemaid ja selgitavamaid nimesid nagu on programmeerimises omane.

Given search key K  and an array A  of n  integers A0, A1, … , An – 1 sorted such that A0 ≤ A1≤ … ≤ An – 1 use the following algorithm to find if K ∈ A

  1. Set lowIndex L  to   and highIndex H  to n − 1 .
  2. Call the recursive function using 4 arguments – A, L, H, K .
    • If L > H , the search terminates as unsuccessful – return 0
    • Set m  (the position of the middle element) to the floor of (L + H)  /  2
    • If Am < K, set L  to m + 1 , call recursion again
    • If Am > K, set H  to m − 1 , call recursion again
    • If Am = K, the search was successful – return 1

Ülesanne valmis, valmista enne esitamist ette vastused järgmistele küsimustele. Kui vastused olemas, anna märku!

  • Ajalike keerukus – võrdle antud algoritmi ja tavalist lineaarset otsingut. Mitu võrdlust läheks vaja, et võrrelda massiivi kus on
    • 16 liiget
    • 256 liiget
    • 100 000 liiget
  • Anna hinnang algoritmi keerukusele  – nt konstantne, logaritmiline, lineaarne, eksponentsiaalne (https://www.bigocheatsheet.com)
  • Kui massiiv poleks sorteeritud, kas see algoritm töötaks?

Edasijõudnute ülesanne 2: pinu laiendus

Selles ülesandes loome paar lisavõimalust oma pinule ning teeme seda veidi universaalsemaks.

Nõuded
  • Lisa oma pinule järgmised funktsioonid
    • Duplicate()
    • Swap()
  • Need funktsioonid tohivad kasutada ainult Push() ja Pop() funktsioonide väljakutseid!
  • Muuda oma Pop() ja Peek() funktsioonide kujusid sedasi, et kehtiksid järgmised nõuded
    • Pinu peab toetama kõiki arve (ei tohi olla reserveeritud mitmetähenduslike väärtusi).
    • Juhul kui tekib pinu alatäitumine, siis kuvatakse vaid veateade, väärtust ennast ei kuvata
  • Märkus: Lisafunktsioonide jaoks pead suure tõenäousega muutma ka Push() funktsiooni!
Testimine

Olulised kitsaskohad mida testida (lisaks tavaolukordadele)

  • Pop(), kui pinu on tühi (ei tohi numbrit väljastada
  • Duplicate(), kui andmeid pinus pole
  • Duplicate(), kui pinu on täis
  • Swap(), kui pinus andmeid pole
  • Swap(), kui pinus on vaid 1 liige

Pärast seda tundi peaksid

  • Teadma mida tähendab rekursioon, sh rekursiivne funktsioon ja rekursiivne andmetüüp.
  • Oskama kirjutada lihtsaid rekursioone
  • Teadma, et eksisteerib ka sabaga rekursioon
  • Teadma rekursioonide headest omadustest ja probleemidest
  • Teadma rekursioonide kasutusvaldkondi
  • Teadma mida tähendab abstraktne andmestruktuur
  • Teadma mis on pinu ning millised on selle omadused
  • Teadma pinu kasutusvaldkondi
  • Oskama luua pinu ning sellele kohustuslike funktsioone

Täiendav materjal

PR2ET8: Dünaamiline mälu 2

Labori materjal

Laboriülesanded

Selles laboris on üks ülesanne, mida laiendab 2 edasijõudnute ülesannet

Ülesanne: Faili lugemine kasutades dünaamilist mälu

Selle ülesande eesmärgiks on tutvuda kuidas lugeda teadmata pikkusega faile sedasi, et me kasutame täpselt nii palju mälu kui failis olevate andmete hoiustamiseks vaja on.

Andmefail

Andmefaili struktuur on järgnev:  <indeks> <perenimi> <eesnimi> <õppekava kood> <punktide arv>

  • Indeks (täisarv)
  • Ees- ja perenimi – erineva pikkusega sõned
  • Õppekava kood – 4-tähemärgi pikkune sõne
  • Punktide arv – kümnendmurd vahemikus 10,0 kuni 30,0,. Täpsuse 0,1.

Võid kasutada andmefailide loomiseks oma eelmise nädala laboriülesande lahendust. Testimiseks on antud siiski kindlad failid, et saaksid oma väljundit võrrelda.

Andmefailid mille väljundi näited on ka testimine osas: https://blue.pri.ee/ttu/files/iax0584/andmefailid/8_scholarship_data.zip

Nõuded
  • Loe teadmata pikkusega andmefaili sisu mällu
    • Lugemiseks kasuta realloc() funktsiooni, laiendades mäluala suurust jooksvalt lugemise käigus.
    • Andmed salvesta dünaamiliselt loodud struktuurimassiivi
    • Lugemise lõpuks peab küsitud mälumaht olema täpselt nii suur kui on vaja failis olevate andmete hoiustamiseks.
    • Faili tohib vaid ühel korral lugeda (korduv lugemine keelatud! Ka lihtsalt reavahetuste arvu lugemine ei ole lubatud!)
    • Arvesta, et faili pikkus võib muutuda! St faili pikkus selgub lugemise käigus!
  • Kõik muutuva pikkusega sõned tuleb mälus hoida täpse pikkusega.
    • Lugemise ajal kasuta staatilist puhvrit, struktuuris hoidmiseks kasuta dünaamilist mälu!
  • Failis on stipendiumile kandideerivate tudengite loetelu. Stipendiumi saavad:
    • Igast järgnevast erialast kuni 7 kõige kõrgema punktisummaga tudengit: IACB, EARB ja MVEB.
  • Kuva stipendiumi saajate nimekiri ning näita mitu tudengit igalt erialalt stipendiumi sai.
  • Veendu, et programm ei tekita mälulekkeid!
Andmestruktuur ülesandele

Selles ülesandes läheme üle dünaamilisele mälule struktuuri liikmete hulgas mille pikkus on muutuv (sõned ja teised massiivid) – see säästab meil mälu ja suurendab paindlikkust. Struktuur võtab järgneva kuju:

Märkused:

  • Soovi korral võid nimetusi endale sobilikumaks muuta
  • fName ja lName on nüüd viidad, mis vajavad dünaamilist mälu enne kui nendesse midagi salvestada saab – seda kuna nime pikkus on muutuv inimeselt inimesele.
  • Õppekava koodi massiiv on staatiline sest see on alati täpselt sama pikk – dünaamiline mälu siin oleks raiskav.
  • Üksikud täisarvud, murdarvud jne jäävad samuti staatiliseks – jällegi, dünaamiline mälu siin teeks programmi põhjendamatult aeglasemaks ja keerukamaks.
Soovituslik loetelu funktsioonidest

NB! Funktsioonide tegelik kuju sõltub kuidas otsustad ülesannet lahendada ja struktureerida.

Minimaalselt on sul vaja kolme funktsiooni:

  • Andmete lugemise funktsioon failist (vali välja järgmisest peatükist).
  • Vastuste väljastamise funktsiooni.
  • Andmete vabastamise funktsiooni

Soovituslikud funktsioonid enda elu lihtsamaks tegemiseks

  • qsordi võrdlusfunktsioon
  • Ühe tudengi andmete printimise funktsioon (kasulik kui prindid tudengi andmeid, kes stipendiumi saab.
  • Kaitsva programmeerimisstiili jaoks kulub ära ka slaididel näidatud vabastamisfunktsioon

Lugemise kontrollimiseks võib olla mõistlik luua ka funktsioon, mis trükib kõigi tudengite andmed välja mis failist kätte saadi. See aga pole ülesande osa.

Funktsioon andmete lugemiseks

Sel korral võtab andmete lugemise funktsioon veidi teistsuguse kuju.  Funktsioonist on meil vaja saada kätte 2 uut väärtust – mälu asukoht ja ridade arv. Pakun välja kolm võimalikku lahendust – vali endale meelepärane!

Esimene variant tagastab ridade arvu ning edastab mälu asukoha kasutades topeltviita :

Teine variant tagastab mälu asukoha ning edastab ridade arvu kasutades viita:

Pakun välja ka kolmanda võimaluse. Selle eelis on võimalus funktsioonist tagastada kood, mida saab tõlgendada kas eduka või ebaõnnestunud lugemisega. Koode võib olla enam kui soovid erinevate vigade detaile edastada.

Testimine

Esimeses näites kasutame pikemat andmefaili, kus kõik loetelud said täis.

Teises näites on andmefail lühem.

Edasijõudnute ülesanne 1: Optimaalne mäluhõive

Selles ülesandes muudame oma dünaamilise mälu hõivamise mõistlikumaks kasutades optimaalsemat mälu laiendamise strateegiat.

Nõuded
  • Muuda oma faili lugemist sedasi, et lugemisek kasutaksid (n * 2) strateegiat
    • St iga kord kui küsitud mälumaht saab otsa, laiendatakse mäluala 2x võrreldes olemasolevaga
  • Alustamiseks vali n mis on suurem kui 1 (vali siiski vähemalt 3x väiksem kui pikima faili pikkus

Edasijõudnute ülesanne 2: ümbris-struktuur

Selles ülesandes muudame oma koodi veelgi loetavamaks ja seotumaks, pannes oma andmestruktuuri ja selle omadused ühte ümbritsevasse struktuuri.

Nõuded
  • Pane oma struktuurimassiiv teise struktuuri ehk ümbrise sisse (wrapper struct),
  • Ümbrises peaks olema 3 muutujat – viide struktuurile, struktuuri suurus ja struktuuri kasutatud liikmete arv.
  • Muuda oma funktsioonide parameetreid nii, et nüüd need kasutaksid uut vastloodud ümbrisstruktuuri.
  • NB! Mõtle läbi mis funktsiooni on mõistlik viit ümbrisele anda, millisesse pole seda mõtet teha!

Vihje: Kuna nüüd on iga välja poole pöördumisel vaja ümbrisstrukuturist valida välja õige liige, siis võib loetavuse huvides olla see mõistlik “lahti pakkida” eraldi muutujasse vastava funktsiooni või tsükli sees. Näiteks

Pärast seda tundi peaksid

  • teadma erinevaid võimalusi kuidas struktureerida ja lugeda faile mille pikkus võib erineda
  • oskama dünaamiliselt oma massiivide suurust muuta
  • oskama lugeda faile dünaamilist mälu kasutades jooksvalt ja täpselt
  • teadma kõiki võimalike realloci tagastuste võimalus
  • teadma kahte laiendamise strateegiat (n + 1) ja (n * 2) ning oskama neid kahte võrrelda
  • oskama struktuuri liikmetele mälu küsida
  • teadma ja oskama rakendada ohutu programmeerimise tehnikat mälu vabastamisel.
  • teadma kõiki samme mida strdup() funktsioon taustal teeb

Täiendav materjal

PR2ET7: Dünaamiline mälu 1

Labori materjal

Laboriülesanded

Sellel nädalal on üks ülesanne, mida laiendab kaks edasijõudnute ülesannet.

Ülesanne: Juhuandmete generaator

Selle nädala ülesandeks on koostada juhuandmetega andmefaili generaator. Selliseid generaatoreid kasutatakse sageli rakenduste testimiseks enne kui on ligipääs reaalsetele andmetele.

Lae alla aluskood: https://blue.pri.ee/ttu/files/iax0584/aluskoodid/7_generator_starter.zip

Nõuded
  • Ehita oma rakendus ette antud aluskoodi põhjale
  • Küsi kasutajalt mitu kirjet ta soovib genereerida. Piirangut olla ei tohi!
  • Kõik kirjed salvestatakse genereerimise käigus struktuurimassiivi, mis luuakse kasutades dünaamilist mälu.
  • Vali juhuslikult etteantud valimitest välja eesnimi, perenimi ja  õppekava kood.
  • Genereeri juhuslikult sisseastumispunktid.
    • Punktid on vahemikus 10 – 30 punkti. Otspunktid on kaasatud. Täpsus on 0,1 punkti (nt 24.7)
  • Sorteeri genereeritud andmed perenime järgi. Kui perenimed ühtivad, sorteeri eesnime järgi.
  • Väljund kirjuta faili järgnevas vormingus:
    <indeks> <perenimi> <eesnimi> <õppekava kood> <sisseastumispunktid>
    • Indeks on unikaalne täisarv. Esimesel kirjel on indeksis 0, igal järgneval suureneb see 1 võrra.
  • Veendu, et kogu mälu on programmi lõppedes vabastatud kasutades valgrind’i.
Qsordi võrdlusfunktsioon

Pakun välja kaks erinevat võrdlusfunktsiooni võimalust. Esimesel puhul tehakse tüübiteindused jooksvalt, likvideerides lisamuutujate loomise vajaduse.

Teisel juhul loome lisaviidad, mis võtavad täiendavalt mälu, kuid lihtsustavad koodi loetavust.

Töövoog
  • Loo vajalik struktuuri kirjeldus
  • Küsi kasutajalt palju kirjeid vaja
  • Küsi mälu vajalike kirjete hoidmiseks, kontrolli!
  • Genereeri vajalikud kirjed
    • Iga isiku jaoks pead genereerima kõik väljad juhuslikult.
    • Iga  struktuuri liikme valimiseks eesnimede, perenimede ja õppekava koodide valimist pead genereerima juhusliku numbri.
      Vihje: Sa võid nime kas kopeerida enda struktuuri või hoiustada vaid viita nimele
    • Lisaks genereeri ja salvesta ka punktid.
      Vihje: Mõtle matemaatiliselt – nt mis vahe on 30 ja 300!?! rand() funktsioon väljastab sulle täisarvu olenemata mida sa teha üritad sellega.
  • Sorteeri struktuurid
  • Kirjuta andmed väljundfaili
  • Vabasta mälu

Kontrolli oma rakendust kasutades valgrindi! Seda mitte ainult lõpus, vaid ka siis kui rakendus käitub imelikult või jookseb kokku!

Testimine

Rakenduse väljund on võrdlemisi lihtne ja lühike

Vaadates väljundfaili näeme, et tulemused on kenasti sorteeritud (näites vaid esimesed 15 rida)

Edasijõudnute ülesanne 1: väljundfaili formaat

Selle ülesande käigus lisame oma lahendusele CSV formaadi toe väljundis ning lubame kasutajal valida sobivlikku väljundformaati.

Nõuded
  • Lisa oma programmile võimekus genereerida andmeid CSV formaadis.
    • Esimene rida CSV failis peab olema päis väljade nimetustega
    • Sellele järgnevad genereeritud andmeread, komadega eraldatud. NB! CSV puhul koma järele tühik ei käi.
  • Küsi kasutajalt kummas formaadis ta soovib faili genereerida (tühikutega eraldatud või CSV) ning genereeri sobilik väljundfail.
  • Tühikutega eraldatud faili laiendiks peab olema .txt , komadega eraldatud faili laiendiks peab olema .csv .
  • Veendu, et CSV fail on korrektselt genereeritud – proovi avada või importida seda kasutades Libreoffice Calc’i või Microsoft Office’it ja vaata kas nad tuvastavad väljad korrektselt.

Edasijõudnute ülesanne 2: seadistused

Selles lisaülesandes muudame oma generaatori paindlikumaks ja lisame seadistuste võimekuse.

Nõuded
  • Kõik seadistused tuleb hoida struktuuris – loo eraldi struktuuri kirjeldus ja struktuur selleks.
  • Kõigil seadistustel peavad olema vaikeväärtused
  • Programm peab kasutama vaikeväärtusi kui kasutaja ei soovi seadeid muuta. Kuidas kasutaja muutmissoovist teada peab anda võid ise otsustada.
    Näiteks võib programm seda esimese asjana käivitudes küsida või saab kasutaja käivitada programmi kindla käsureaargumendiga, mis seadistuse avab.
  • Kasutajal peab soovi korral olema võimalik järgnevaid seadistusi muuta programmis sees olles
    • Millised andmeväljad genereeritakse (igaühte peab olema eraldi võimalik sisse-välja lülitada)
    • Väljundfaili nimi (ainult nimeosa, laiend valitakse automaatselt lähtuvalt valitud väljundformaadist!)
    • Väljundformaadi seadistus (edasijõudnute ülesande 1 osa, tõsta struktuuri sisse)
    • Genereeritavate kirjete arv (baasülesande osa, tõsta struktuuri sisse)
    • Alam- ja ülempiir sisseastumispunktidele
  • NB! Genereeritavate kirjete arvu tuleb küsida hoolimata sellest kas kasutaja soovis seadeid muuta või mitte. Eesmärk on lihtsalt hoida seadistusi koos.
  • Kasutajale tuleb kuvada genereerimisseaded olenemata sellest kas ta soovis neid muuta või mitte.

Märkus: Soovi korral võid seadistusi hoida eraldi failis, kuid see pole vajalik. Kasutaja tehtud muudatusi seadetesse ei ole vaja meeles pidada järgmisel programmi käivitamisel. Küll aga kui sa soovid seadefaili kasutada, siis veendu, et kasutaja saab seadistusi muuta läbi programmi (ilma, et ta peaks seadefaili käsitsi muutma).

Vihje: Siin saab kolmikoperaatorit hõlpsasti ära kasutada
printf("First name: %10s\n", settings.genFirstName ? "Yes": "No");

Pärast seda tundi peaksid

  • Oskama kasutada dünaamilist mälu
  • Oskama kontrollida kas programmis on mälulekkeid
  • Teadma mis olukorras on dünaamilist mälu mõistlik kasutada ja millal mitte
  • Teadma dünaamilise mälu võludest ja valudest
  • Tegema vahet pinumälul ja kuhjal ning mis muutujad kuhu lähevad.

Täiendav materjal

PR2ET6: Makefile ja logimine

Labori materjal

Laboriülesanded

Selles laboris on üks ülesanne, mis on jagatud kahte ossa. Ülesannet laiendab kaks edasijõudnute ülesannet.

NB! Kuigi arendada saad oma lahendused ükskõik mis platvormil, siis üks osa tõestusest vajab Valgrind tööriista kasutamist. Valgrind töötab kontrollitult vaid Linuxil (rakendus olemas ka MacOSil) – vajadusel saad rakenduse tõestuseks üles laadida kooliarvutisse.

Lae alla andmefailid: https://blue.pri.ee/ttu/files/iax0584/andmefailid/6_make_data.zip

Arhiiv sisaldab andmefaile nii baasülesandeks kui edasijõudnute ülesandeks!

Ülesande osa 1 / 2: Andmete lugemine, töötlemine

Esimeses osas on meie eesmärgiks saada programmi andmed sisse ning demonstreerida oma oskusi Makefile’i koostamiseks ja kasutamiseks ning mälus vigade puudumiseks.

Nõuded
  • Loe andmed sisendfailist
    • Andmefaili struktuur
      <õppeaine nimi> <hinnete arv> <hinded>
    • Loetud andmed salvesta struktuurimassiivi. Hinnete jaoks võid teha mõistliku pikkusega massiivi – näiteks max 20 hinnet aine kohta (lähtuvalt näitefaili struktuurist).
  • Kuva ekraanil
    • Õppeaine nimi
    • Õppeaines antud hinded
    • Õppeaine keskmine hinne
  • Hoia andmete lugemine, keskmise leidmine ja väljastus eraldi funktsioonides!
  • Kood peab olema tükeldatud vähemalt kahte koodifaili, millest mõlemal on oma päisefail. Tükeldus peab olema mõistlik, kuid on jäetud sinu otsustada.
  • Kood kompileeri programmiks kasutades Makefile’i
    • Makefile’is peab olema minimaalselt kirjeldatud retsept all , muutuja CFLAGS  ja lipud -Wall -Wextra -Wconversion -g
    • Ülejäänud Makefile’i keerukuse ja ülesehituse võid ise otsustada
  •  Kaitsmisel
    • Näita projekti kompileerimist kasutades Makefile’i
    • Näita programmi väljundit läbi Valgrindi tõestamaks, et puuduvad mäluvead.
Vihje

Hinnete arv aine kohta varieerub – st iga failis olev andmerida võib olla erineva pikkusega. Seetõttu andmefaili võimalik lugeda ühe ühte fscanf()  funktsiooni väljakutsega!

Lugemine on vaja teha pesastatud tsüklitega, kus

  • Välimine tsükkel loeb õppeaine nime ning hinnete arvu
  • Vastavalt välimisest tsüklist saadud hinnete arvule määratakse ära sisemise tsükli korduste arv, lugemaks sisse õppeaines antud hinded. Ära unusta sisemise tsükli fscanf()  funktsiooni tagastust kontrollida!
Testimine

Näide võimalikust koodi struktuurist.

NB! Failide nimed ei ole ette määratud, tegu on lihtsalt näidisega. Samuti piisab vaid ühest andmefailist ning see ei pea olema eraldi kaustas.

NB! Järgnevas näites ei ole kajastatud valgrindi väljundit! Ära unusta testida oma programmi korrektsust valgrindiga!

Ülesande osa 2 / 2: Logimine

Selle osa raames tuleb sul lisada oma programmi logimine.

Nõuded
  • Lisa oma programmi logimine
    • Üks logi rea kohta. Logi ei tohi olla kaherealine!
    • Iga logi rida algab ajatempliga. Ajatempel peab sisaldama kuupäeva (päev, kuu, aasta) ja kellaaega (tunnid, minutid, sekundid)
  • Programmi järgmine käivitus ei tohi kustutada eelneva käivituse logi
  • Logitavad sündmused
    • Programmi käivitamine ja sulgemine
    • Sisendfaili avamise seisund (edukas / ebaõnnestunud)
    • Faili lugemine lõpetatud. Logis kuva loetud ridade arv!
    • Faili lugemine katkestatud kuna massiivi maksimaalne piir on ületatud (maksimaalne piir kuvatakse logis).  NB! Sul on struktuuride massiiv ja struktuuri liikmena hinnete massiiv!
    • Viga aine keskmise hinde arvutamisel.
  • Logi terviklikkus peab olema garanteeritud ka programmi kokkujooksu korral
    • Võimalus 1: Väljundpuhvrist on võimalik sunniga andmed faili kirjutada kasutades funktsiooni fflush()
    • Võimalus 2: Sulge logifail pärast kirjutamist – faili sulgemise järel kirjutatakse puhvris olevad andmed faili.
  • Logifaili avamise ebaõnnestumisel peab programm jätkama tööd, mitte väljuma! Teavita kasutajat logimise ebaõnnestumisest.
Vihjeid
  • Valmista ette logitav sõne enne logimise funktsiooni väljakutsumist! Ära koorma logimise funktsiooni üle parameetritega. Sõne ettevalmistamiseks on hea funktsioon snprintf()
  • Logifunktsiooni lihtsustamiseks on soovitatav mitte failiviita ega nime sellele kaasa anda. Võimalike lahendusi:
    • Ava ja sulge fail logifunktsioonis järjest
    • See on üks võimalikest eranditest globaalmuutujale – soovi korral võid teha failiviida logifailile globaalmuutujana. (See ei ole hea lahendus, kuid aktsepteeritav – parem võimalus on kirjutatud lahti edasijõudnute ülesandena).
  • Näiteks võiks logimise väljakutsumine välja näha nii:
    Logger("Program started");

Edasijõudnute ülesanne 1: Logimise teek

Selle ülesande raames lood sa endale logimise teegi. Vihje: seda saad ära kasutada kodutöö 2 juures!

Nõuded
  • Lisa programmile eraldi .c ja .h fail ja tõsta sinna logimine
  • Lisa 4 logimise taset – OFF, ERROR, WARNING ja INFO – need on loendi tüüpi (enum).
    • Iga logifaili rea juures on kirjeldatud selle logi tase.
    • Logi kirjutatakse faili vaid siis, kui määratud tase seda lubab. Näiteks:
      INFO taseme korral kirjutatakse kõigi tasemete logid;
      WARNING taseme korral kirjutatakse ainult WARNING ja ERROR tasemega logid;
      ERROR taseme korral kirjutatakse ainult ERROR tasemega logid;
      OFF korral logisid ei kirjutata;
  • Iga kutse logi kirjutamiseks peab nüüd sisaldama selle logi olulisuse taset (ERROR, WARNING või INFO)
    Näiteks: Logger(LOG_INFO, "Program started");
  • Teek peab toetama logi väljundfaili nime andmist
    Näiteks: LoggerSetOutputName("log.txt");
  • Teek peab toetama programmi logitaseme sättimist
    Näiteks: LoggerSetLoggingLevel(LOG_INFO);
  • Logimise seadistused tuleb hoida logimise teegi sisemiselt. Selleks on logifaili nimi ja logi tase (võid ka lisada täiendavaid). Need hoiusta logi koodifailis globaalmuutujatena.
  • Logifaili seadistustel peavad olema vaikeväärtused – näiteks kui kasutaja unustab või ei soovi neid seadistada, siis programm ei läheks katki.
  • Iga logi peab sisaldama kellaaega nii nagu oli kirjeldatud ülesande teises osas.

Edasijõudnute ülesanne 2: Lihtsustatud CSV failist lugemine

Selle edasijõudnute ülesande eesmärgiks on võimaldada mitmest sõnast koosnevaid õppeainete nimesid.

Nõuded
  • Kasuta sisendfailina CSV versiooni sisendfailist
  • Andmeväljad on üksteisest eraldatud komaga. Andmeväljad ise komasid ei sisalda.
Lahenduskäik

NB! Kasutame lihtsustatud metoodikat CSV lugemiseks. Sellega ei ole võimalik lugeda ükskõik missugust CSV faili! St kui andmeväli võib sisaldada ka komasid, siis seda metoodikat kasutada ei saa!

Kui vajad täielikku CSV tuge, kasuta kas vastavat teeki (nt libcsv) või kirjuta näiteks olekumasina-põhine töötlemine (loed tähemärk-haaval ja otsustad mida teha lähtuvalt loetud tähemärgist ja hetkeolekust olekumasinas)


scanf() funktsioonide perekond võimaldab lisaks andmeformaadi kirjeldamisele ka kirjeldada oodatavat sisendit ning lihtsamaid regulaaravaldisi.

Näiteks lugemaks kasutajalt kellaaega, saame kasutada formaati scanf("%d:%d", &hours, &minutes);  – sellisel juhul saab kasutaja sisestada kellaaja formaadiga 14:35 . Muutuja hours  saab väärtuseks 14 ja minutes  väärtuseks 35 . Kui aga kasutaja sisestab 14 35 , siis hours  saab ikka väärtuseks 14 , kuid kuna tühik polnud oodatud formaat, siis sealt edasi ei loeta ning minutes  jääb väärtustamata.

Lähtuvalt sellest teadmisest saame koostada faili lugemiseks formaadi fscanf(fp, "%[^,],%d", ...)  . Asenda failiviida nimi enda kasutatud viidaga ning kolme punkti asemele pane oma muutujad kuhu andmeid salvestad.

  • Esimene väli loetakse tekstina kuniks komani (koma ei loeta). See salvestatakse tekstimassiivi (õppeaine nimi)
  • Seejärel loetakse puhvrist välja koma (ja visatakse ära)
  • Siis loetakse sisse üks täisarv (hinnete arv aines)

Sedasi saad loetud esimesed 2 välja. Edasi pead juba formaadi koostama hinnete lugemiseks (ära komasid unusta!).

Pärast seda tundi peaksid

  • Teadma erinevate ehitussüsteemide kohta
  • Oskama koostada lihtsat Makefile’i
    • Teadma kuidas deklareerida muutujaid ja neid kasutada
    • Teadma kuidas koostada retsepti, sh mitut retsepti ühes failis
    • Teadma mis retsepti sees käib
    • Teadma varjatud reeglitest ja oskama neid vajadusel kasutada
  • Oskama kasutada Makefile’i programmi kompileerimiseks
  • Oskama kasutada valgrindi programmist vigade leidmiseks ja korrektsuse kontrollimiseks
  • Oskama programmi siseselt hetke kellaaega leida ning seda kujundada
  • Teadma logimise põhitõdesid ja olulisust

Täiendav materjal

 

PR2ET5: Qsort ja tükeldus

Labori materjal

Laboriülesanded

Selles laboris on kaks ülesannet.

  • Esimene ülesanne koosneb kahest baasosast ja edasijõudnute lisast.
  • Teine ülesanne koosneb baasülesandest ja edasijõudnute lisast.

Ülesanne 1: Quicksort

Selle ülesande eesmärk on harjutada qsort()  funktsiooni kasutamist ning koodi tükeldamist erinevate koodifailide vahel, sealjuures alustad oma esimese kergesti lisatava teegifaili loomist. Aluskoodis on juba koodi tükeldus failidesse sinu eest juba tehtud.

Lae alla aluskood: https://blue.pri.ee/ttu/files/iax0584/aluskoodid/5_1_qsort_task_basecode.zip

NB! Aluskood sisaldab tükke mõlema baasosa ja edasijõudnute osa jaoks!

Nõuded
  • Realiseeri vajalikud funktsioonid mis on ette antud sulle erinevates päisefailides prototüüpidena. Funktsiooni realisatsioon peab olema päisefailiga samanimelises .c koodifailis
  • Kutsu välja vajalikud funktsioonid andmete sorteerimiseks ja ekraanile kuvamiseks main funktsiooni switch lausest.
  • Kompileeri programm kokku mitmest koodifailist – vastavalt struktuurile mis oli antud aluskoodis. Kasuta kompileerimiseks käsurida (võid kasutada ka Makefile’i kui oskad).
Ülesande hinnatavad alamosad

Esimese hinnatava osa jaoks peavad töötama menüüst valikud 1 ja 2. Selle sooritamiseks pead sa

  • Realiseerima qsort võrdlusfunktsioonid täisarvudest ja murdarvudest koosneva massiivi jaoks
  • Kutsuma välja qsort funktsiooni mõlema eelnimetatud massiivi sorteerimiseks ning väljatrükiks menüüs

Teise hinnatava osa jaoks peavad töötama menüüst valikud 3 ja 4. Selle sooritamiseks pead sa

  • Realiseerima qsort võrdlusfunktsioonid struktuurimassiivi sorteerimiseks eesnime ja töösuhte pikkuse järgi
  • Realiseerima struktuurimassiivi väljatrüki funktsiooni.
  • Kutsuma välja qsort funktsiooni struktuurimassiivi sorteerimiseks vastavalt menüü valikule ning seejärel massiivi väljatrüki funktsiooni

Edasijõudnute ülesande hinnatava osa jaoks peab töötama menüüst valikud 5. Selle sooritamiseks pead sa

  • Realiseerima qsort võrdlusfunktsioonid struktuurimassiivi sorteerimiseks perenime järgi. Kui perenimed on samad, tuleb teha täiendav otsus sorteerimiseks eesnime alusel.
  • Kutsuma välja qsort funktsiooni struktuurimassiivi sorteerimiseks vastavalt menüü valikule ning seejärel massiivi väljatrüki funktsiooni
  • Kutsuma välja qsort funktsiooni struktuurimassiivi sorteerimiseks vastavalt menüü valikule ning seejärel massiivi väljatrüki funktsiooni
Testimine

Järgnev näide sisaldab kõiki kolme hinnatavat osa (1 ja 2 baasosa ning edasijõudnute osa)

Ava mind väljundi nägemiseks
 

Ülesanne 2: Teekide kasutamine (covid-data)

Selles ülesandes tutvume esimest välise teegi (libcurl) lisamisest oma programmi, mille abil on võimalik näiteks kasutada internetis olevaid APIsid ja faile internetist alla laadida. Kasutame neid Eesti riigi avaandmete alla laadimiseks. Jätkame programmi tükeldamise harjutamisega.

NB! Ülesande lahendamine on kordades lihtsam UNIX põhistel süsteemidel (Nt Linux). Windowsil ülesande lahendamiseks on vaja teha täiendav seadistus ning muuta osa ette antud lähtekoodist!

Lae alla aluskood: https://blue.pri.ee/ttu/files/iax0584/aluskoodid/5_2_covid_starter.zip

Nõuded
  • Kasuta põhjana sulle antud aluskoodi
  • Lae alla Eesti Covid-19 avaandmed (ette antud aluskoodis)
    NB! Avaandmeid uuendatakse korra nädalas! 
  • Avaandmete spetsifikatsioon
  • Kuva viimase 14 päeva kinnitatud nakatumised (päev ja nakatumiste arv)
  • Kuva top10 kõige suurema nakatumiste arvuga päeva (päev ja nakatunute arv)
  • Minimaalselt peab olema kood tükeldatud kolme koodi ja päisefaili
    • file_helper.c  ja file_helper.h  on faili allalaadimiseks ja ettevalmistuseks  (ette antud, muudatusi pole vaja teha)
    • data_processor.c  ja data_processor.h  on failist andmete lugemiseks ja töötlemiseks
    • main.c  ja main.h  on programmi töövoo juhtimiseks ja üldisteks makroteks
    • Soovi korral võid näiteks faili lugemise lüüa lahku andmete töötlemisest
  • Kompileeri kõik koodifailid kokku kas käsurealt või kasutades Makefile’i
Soovituslik töövoog
  1. Tutvu etteantud koodifaiide struktuuri ja sisuga
  2. Kommenteeri sisse enda eelistatud andmefaili formaat (kas tühikute või komadega eraldatud andmed)
  3. Kompileeri ja proovi kas allalaadimine töötab. Kompileerimiseks on vajalik lipp -lcurl
  4. Lisa main.h  faili struktuuri kirjeldus andmete mälus hoidmiseks ning teised vajalikud makrod suurustele
    Vihje: sul ei ole vaja kõike failist loetut mälus hoida! Vali välja vaid olulised väljad!
  5. Kodeeri data_process.c  faili andmefailist andmete lugemise funktsioon. Lisa selle funktsiooni prototüüp data_process.h  päisefaili ning väljakutse main funktsiooni.
    NB! Kas märkasid, et CSV failil oli esimene rida päis!?
    Vihje: konkreetse andmevälja ignoreerimiseks saad scanf  funktsioonis kasutada formaati %*s  – tärn tähistab,  andmeid sellele formaadile vastavale osale ei salvestata. St ignoreeritakse kõike kuni järgmise tühiku või reavahetuseni. Kui kasutad tärni formaadis ei tohi selle kohta muutujat scanf  parameetrina anda.
    Kompileeri ja testi kas andmed loetakse edukalt sisse
  6. Loo funktsioon andmete väljatrükiks, et kuvada viimase 14 päeva statistika.
    Vihje: Kasuta ära oma teadmist massiividest ja viida-aritmeetikast mida rakendasime esimesel nädalal viitade tunnis (ülesanne 1, osa 2)! Nii jääb väljatrüki funktsioon lihtsaks ja korduvkasutatavaks järgmise punkti jaoks.
  7. Loo qsort  jaoks võrdlusfunktsioon ning kutsu qsort  välja andmete sorteerimiseks. Kuva top 10 kõrgeima nakatunute arvuga päeva.
    Vihje: Kui kasutasid eelmise punkti juures viida-aritmeetika ja jätsid väljatrüki funktsiooni lihtsaks, siis nüüd saad seda sama funktsiooni ka siin ära kasutada!
Testimine

NB! Väljundis on koos baasülesande ja edasijõudnute ülesande lahendused!

Edasijõudnute ülesanne
  • Leia nakatumiste arv viimase 7 päeva jooksu
  • Leia nakatumiste arv sellele eelneva 7 päeva jooksul
  • Väljasta perioodi vahemikud ja nakatunute koguarv neil perioodidel
  • Leia ja kuva kas nakatumine kahe võrreldava perioodi vahel on vähenenud, kasvanud või jäänud samaks. Näita protsenti kahe komakohaga.

Vihje: Baasülesande raames sorteerimise andmemassiivi terviklikult ära ja neid andmeid me kasutada ei saa ilma uuesti sorteerimata. Küll aga saame enne sorteerimist teha osalise koopia andmetest. Koopia saad teha (enne kui qsort välja kutsutakse) kasutades funktsiooni memcpy() . Uuri parameetreid ja meenuta viida-aritmeetikat alguspunkti määramiseks!

MS Windows

Antud ülesannet on võimalik lahendada ka Windowsil, kuid selleks tuleb teha mõned täiendavad liigutused. Sammud on testitud ja töötasid kevadel 2022 kasutades chocolatey kaudu installeeritud 64 bitist MinGW-d (see oli osa lihtsast tarkvara paigaldamise juhendist Windowsil kasutades käsurida). Väljavõte eelmise aasta vestlusest, kus sammud kirja said (inglise keeles)

1. Install libcurl on Windows. Uses chocolatey package manager

choco install curl

2. Make the library (dll file) available to the compiled program. Copy libcurl-x64.dll from libcurl installation directory to your program directory (assumes 64-bit compiler).

3. You need to download CA certificate so cURL can verify the certificate on the opendata website https://curl.se/ca/cacert.pem.

4. You need to specify the certificate authority file location to the code. Add this to the setup of the download in the file_helper.c, replacing the path\\to\\file with the path to the downloaded .pem file.

curl_easy_setopt(curl_handle, CURLOPT_CAINFO, "path\\to\\file");

5. You need to rewrite the check if a file is already downloaded because that uses POSIX library available on UNIX systems – unistd.h. You should rewrite it with windows solution of that. Haven’t tested, but you’ll likely find the function BOOL FileExists(LPCTSTR szPath) in the library io.h. You may need to add additional Windows-specific libraries.

6. You can only download as CSV with the code provided. To download space delimited, you need to rewrite the ReplaceChars function. Easy option would be to open up a second file pointer for writing to a different file and remove the fseek, Simplest would be to do getchar -> putchar with a check in between for commas to be replaced with spaces.

7. You need to add -I and -L flags to your compiler command line arguments. Mine were:

-I C:\ProgramData\chocolatey\lib\curl\tools\curl-7.81.0-win64-mingw\include\ -L C:\ProgramData\chocolatey\lib\curl\tools\curl-7.81.0-win64-mingw\lib\

Pärast seda tundi peaksid

  • Oskama tükeldada oma programmi mitme koodifaili vahel mitmest koodifailist koosnevat programmi kompileerida
  • Oskama kasutada qsort funktsiooni
  • Mõistma qsort funktsiooni võrdlusfunktsiooni nõudeid ning suutma kirjutada võrdlusfunktsiooni lihtsatele massiividele ning ka struktuurimassiividele
  • Teadma viitade eksisteerimisest funktsioonidele ning oskama funktsiooni edastada viita teisele funktsioonile
  • Teadma üldistatult samme mis on vajalikud kolmandate osapoolte teekide kasutamiseks ning oskama neid oma programmi kompileerida

Täiendav materjal