Praktikumi materjal
- Slaidid: Qsort
- Slaidid: Koodi tükeldamine
- Koodinäide: Andmetüübist sõltumatu mullsorteerimine
- Näidisprojekt koodi tükeldamisest: https://blue.pri.ee/ttu/files/iax0584/demokood/division_to_files.zip
Praktikumi ülesanded
Selles praktikumis on kaks ülesannet.
- Esimeses ülesandes õpid sorteerima arvumassiive ning struktuuridest koosnevaid massiive ja koodi tükeldama
- Teises ülesandes õpid andmeid internetist alla laadima
Mõlemat ülesannet laiendab lisaülesanne
Ülesanne 1 [W05-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 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 nii baasülesannet kui ka lisaülesannet!
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).
Soovitusi lahendamiseks
- Tutvu arhiivis oleva koodi struktuuriga
- Realiseeri alustuseks qsort võrdlusfunktsioon täisarvude võrdlemiseks.
Vihje: Testimaks ühe faili põhiselt (nt array_helpers.c) kood sai korralikult kirja, võid vajutada “compile” nuppu – selle põhjal tehakse objektfail ainult käesolevast failist, ignoreerides ülejäänud programmi. Sedasi saad kiirelt vigu lokaliseerida ühe faili sees. - Lisa qsort väljakutse menüüsse, ehita kogu projekt ning proovi kas töötab
- Korda sama murdarvuliste massiivi sorteerimiseks (menüü valik 2). Arvesta, et pead leidma ka murdarvudest koosneva massiivi pikkuse ning lisama väljastamise väljakutse
- Nüüd liigu struktuuride juurde. Alusta struktuuri väljastamise funktsioonist. Kirjuta see valmis ning lisa väljakutse menüüsse, testi
- Struktuuride võrdlusfunktsioone kirjutades arvesta järgmist
- Võrdlusfunktsiooni edastatakse
void tüüpi viidad on struktuurile endale (st struktuuri esimese baidi mäluaadress), mitte väljale, mille alusel sina soovid sorteerida
TÜÜPVIGA: Teisendus peab toimuma struktuuri tüüpi viidaks, mitte int või float vms tüüpi viidaks
Miks? sorteerimisel sorteeritakse terveid struktuure korraga. qsort ise ei tea mis välja alusel sorteerimist tuleks teha ega oska seda välja valida sinu eest. Samuti järjestamise vältel tuleb ümber paigutada kõigi struktuuri liikmete andmed, mitte ainult üks alamväli. - Ole ettevaatlik sulgude paigutusega – tehete järjekord on ülioluline! Esmalt tuleb võrdlusfunktsiooni parameeter teisendada sobilikuks struktuuri tüüpi viidaks. Alles seejärel võib struktuuri sees oleva välja poole pöörduda.
- Mall teisendamiseks: ((struct MyStruct *)x)->member
- Võrdlusfunktsiooni edastatakse
void tüüpi viidad on struktuurile endale (st struktuuri esimese baidi mäluaadress), mitte väljale, mille alusel sina soovid sorteerida
Testimine
Ülesanne 2 [W05-2]: Väliste teekide kasutamine (Tallinna busside väljumised)
Selles ülesande eesmärk on näidata, et programmid saavad suhelda ka internetiga. Selleks tuleb kõigepealt programm liidestada välise teegiga libcurl, mis on laialdaselt kasutusel võimaldades suhelda APIdega, laadida faile alla jne. Ülesande lahendamiseks kasutame Tallinna Transpordiameti poolt pakutavaid avaandmeid. Lisaks peate looma ja kompileerima projekti, mis koosneb mitmest koodi- ja päisefailist.
Selle lihtsustamiseks on tehtud esialgne koodijaotus ning internetist failide allalaadimiseks ja kettale salvestamiseks vajalik kood on juba loodud. Suurem osa sellest on libcurl teegi arendajate avalik näidiskood. Kood on pakitud täiendava turvakihiga, et vältida avalike andmete kuritarvitamist, piirates seejuures värskenduste allalaadimise sagedust. Ära eemalda leech-kaitsekoodi. Andmete liiga sage küsimine toob tõenäoliselt kaasa teenusest blokeerimise.
Kirjutatud kood ja tehtud lihtsustused on loodud Linuxi jaoks. Kui soovid seda koodi Windowsis käivitada, tuleb faili käsitlemise ja tuvastamise osad koodis ümber kirjutada.
Lae alla aluskood: https://blue.pri.ee/ttu/files/iax0584/aluskoodid/5_2_timetable_starter.zip
Nõuded:
- Kasuta etteantud aluskoodi ja järgi faili struktuuri
- Lae alla Raja peatuse andmed
- Väljasta praegune kellaaeg (kasuta peatuse andmetes olevat kellaaega)
- Väljasta busside väljumisajad bussipeatusest
- Minimaalselt tuleb kuvata liininumber, väljumise aeg, sihtkoht (lõpp-peatus) ja aeg bussi saabumiseni (GPS-andmete põhjal)
- Lisa hilinemise hoiatus, kui buss hilineb üle ühe minuti
- Ära näita busse, mis on juba väljunud
- Väljumisaeg peab olema formaadis h:mm:ss
- Uuenda järelejäänud aega korra sekundis
- Minimaalselt peab kood olema jaotatud kolmeks koodi- ja päisefailiks
- file_helper.c ja file_helper.h sisaldavad andmete allalaadimise ja ettevalmistamise koodi (antud algkoodis olemas, muuta ei ole vaja)
- data_processor.c ja data_processor.h sisaldavad andmete lugemise ja töötlemise koodi.
- main.c ja main.h sisaldavad programmi töövoo juhtimist ja üldisi makrosid.
- Soovi korral võid faili lugemise ja andmete töötlemise eraldada täiendavatesse failidesse.
- Kompileeri kõik koodifailid kokku kas käsurealt või kasutades Makefile’i
Soovituslik töövoog
Samm 0: Ettevalmistus
See on vajalik ainult siis, kui kasutad enda arvutit! Kooliarvutit kasutades pole ettevalmistus vajalik!
Installi libcurl teek. Debianil põhinevates süsteemides, sealhulgas Ubuntu ja Linux Mint, tuleb käivitada järgmine käsk:
|
1 |
sudo apt install libcurl4-openssl-dev |
Teiste distributsioonide puhul, mis ei kasuta apt-i, kasuta oma distributsioonile vastavat paketihaldurit.
See paigaldab vajaliku teegi, millega C-programm suhtleb failide internetist allalaadimiseks.
Samm 1: Leia Raja peatuse ID
Peatuse ID on täisarv, mis jääb tavaliselt vahemikku 100 kuni 10 000. Enamik Tallinna peatuste ID-d jäävad vahemikku 100 kuni 2000.
Tähelepanu: Mitmel peatusel on sama nimi – nt Keemia peatusel on kaks erinevat peatuse ID-d, millest üks on suunaga kesklinna ja teine on suunaga bussiparki. Samamoodi on Raja peatusel üks peatus IT-maja ees ja teine IT-maja kõrval. Mõlemal on erinev ID väärtus.
Eeldatud on, et leiad õige peatuse ID käsitsi. Programmi abil stopid väärtuse leidmine jääb ülesande skoobist välja.
Vajaliku peatuse leidmiseks on kaks võimalust. Näidetes on toodud, kuidas leida Väike-Õismäe peatuse ID, milleks on 844.
- Kasuta peatuste andmeid avaandmete portaalist: https://andmed.eesti.ee/datasets/tallinna-uhistranspordi-peatused-ja-marsruudid
Otselink faili, mis sisaldab nimesid ja ID-sid: https://transport.tallinn.ee/data/stops.xml
Õige peatuse ID leidmiseks tuleks vaadata liininumbreid ja nende sõidusuundi.
- Mine transport.tallinn.ee lehele ja ava oma brauseris arendaja tööriistad (kiirklahv F12). Seejärel ava vahekaart Network. Nüüd on sul kaks võimalust: kliki kaardil bussipeatusel ja vali väljumisaegade kuvamine; või vali sõiduplaanist buss ning klõpsa bussipeatuse nimele. Lõpuks otsi päringut nimega “siri-stop-departures” – see sisaldab parameetrit stopid=, kust leiad peatuse ID.

Samm 2: Andmefaili allalaadimine
Failide allalaadimise kood on juba valmis. Kasutame libcurl teegi dokumentatsioonist pärit näidiskoodi, millele on lisatud kaitsemehhanismid, et programm ei koormaks serverit üle, kui peaksid kogemata käivitama allalaadimisfunktsiooni liiga tihti.
Faili allalaadimiseks kutsu välja funktsioon DownloadFileToDisk() . Funktsioon on kirjeldatud failis file_helper.h ja implementeeritud failis file_helper.c . Kasutusjuhised leiad päisefailist. Esimese parameetrina tuleb sul koostada URL, mis sisaldab stopid väärtust. URL peab olema defineeritud failis main.h .
Seejärel kompileeri programm koos kõigi kolme lähtefailiga ja käivita see. Kasuta lippu -lcurl et linkida libcurl teek (nt gcc -o transport_app file1.c file2.c file3.c -g -Wall -Wextra -Wconversion -lcurl ). Seejärel laetakse fail alla. Kontrolli käsitsi, et fail on kettale salvestatud (samas kataloogis, kus programm asub).
Samm 3: Andmete lugemine ja salvestamine
Failist saadud andmed tuleb lugeda struktuurimassiivi. Alusta sobiva struktuuri defineerimisest. Kõiki andmefailis olevaid välju ei ole vaja salvestada – osa neist on ülesande jaoks liiased.
Kirjuta struktuuride deklaratsioonid faili data_processor.h . Selles failis peavad olema ka kõikide failis data_processor.c realiseeritud funktsioonide prototüübid. Nende hulka kuulub ka andmefaili lugemise funktsioon. Vajadusel võid lisada abifunktsioone andmete eeltöötlemiseks.
Andmefaili lugemine on tavapärasest veidi keerulisem, sest fail sisaldab kahte päiserida, millele järgnevad sõidukite väljumised. Soovituslik on andmete lugemise ajal need ka ekraanile kuvada, et veenduda andmete lugemise korrektsuses.
Vihje 1: Faili kaks esimest rida sisaldavad päist, millega tuleb enne lugemistsükli algust tegeleda. Esimene rida sisaldab ajatemplit, mida saab kasutada leidmaks kui palju aega on väljumiseni jäänud. Kasuta seda arvutustes hetkeajana. See ei ole täiesti täpne, kuid piisavalt lähedane. Aja lugemiseks tühikutega eraldatud failist võid kasutada järgmist lugemislauset:
|
1 |
int ret = fscanf(fp, "%*s %*s %*s %*s %d %*[^\n]\n", currentTime); |
Kontrolli kindlasti tagastustatavat väärtust, et veenduda, kas andmed loeti korrektselt.
Vihje 2: Faili teine rida ei ole ülesande jaoks oluline (see kinnitab stopid väärtust). Võid selle vahele jätta, kasutades varasemalt õpitud võtteid, nt: fscanf(fp, "%*[^\n]\n");
Seejärel jätka lugemistsükliga, mis peaks olema sulle tuttav selle semestri varasematest praktikumiülesannetest. Soovitatav on mittevajalikud väljad lugemisel vahele jätta, kasutades formaati %*s (nagu näidatud currentTime lugemise näites).
Samm 4: Sõiduplaani väljastamine
Alustuseks väljasta sõiduplaan ühe korra. Peale kuvamist peaks programm tavapäraselt sulguma. Loo funktsioon, millele antakse parameetritena praegune aeg ja struktuur, mis sisaldab väljuvate sõidukite sõiduplaani andmeid. See peaks olema midagi sellist
|
1 |
void PrintTimetable(struct BusStopData *data, int n, int currentTime) |
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\
Lisaülesanne 1 [W05-3]: Mitme struktuuri liikme põhjal sorteerimine
Lisaülesande lahendamiseks peab olema realiseeritud menüü valik 5 jaoks vajaminev funktsionaalsus. Selleks pead:
- 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 väljund sisaldab vaid lisaülesande tulemust
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
risto@risto-wk-tux:~/pr2/lab/wk5_div/t1_qsort/solution$ ./qsort Select your action! 1. Sort and display integer array (intArr) 2. Sort and display float array (floatArr) 3. Sort and display structures ordered by employment length 4. Sort and display structures ordered first name 5. Sort and display structures ordered by last and first. 0. Exit > 5 Employees sorted by employee last and first name: Anneli Oja 7.30 0 Andres Rebane 22.50 10 Doris Rebane 10.20 3 Mark Rebane 10.30 5 Sirje Vakra 15.40 0 Select your action! 1. Sort and display integer array (intArr) 2. Sort and display float array (floatArr) 3. Sort and display structures ordered by employment length 4. Sort and display structures ordered first name 5. Sort and display structures ordered by last and first. 0. Exit > 0 risto@risto-wk-tux:~/Nextcloud/work/ttu/teaching/_generic/prog2/lab/wk5_div/t1_qsort/solution$ |
Lisaülesanne 2 [W05-4]: Sõidukite asukohad
Lisaülesande lahendamiseks pead alla laadima ka teise andmefaili, mis sisaldab kõigi sõidukite GPS asukohakordinaate.
- Lae GPS asukohti alla perioodiliselt. Aadressi leiad samuti arendaja tööriistu kasutades või avaandmete portaalist.
Soovitav andmete uuendamisintervall on 5 .. 10 sekundit - Leia ICT hoone asukohakordinaadid
- Arvuta kõigi sõidukite kaugus ICT hoonest
- Järjesta sõidukid kauguse järgi kasvavas järjekorras
- Uuenda väljundit perioodiliselt.
- Piira väljudnite arvu nii, et nii baas- kui lisaülesande väljundid mahuksid mõistlikult ekraanile
NB! Uuendamise intervalli miinimumaeg on 15 sekundit, mis on antud aluskoodiga kaasa. Muuda see lühemaks, et võimaldada asukohti tihedamalt alla laadida.
Testimine
Märka, et videos uuendatakse asukohakordinaate ning kaugust hoonest ligikaudu iga 5 sekundi tagant. Kõigi sõidukite asukohad ei muutu – näiteks osad võivad parasjagu paikneda lõppjaamas, oodates oma väljumisaega.
Vihjed
- Kauguse leidmiseks kahe punkti vahel maakeral on mitmeid valemeid. Haversine on üks võimalikest – see pole küll kõige täpsem, kuid on võrdlemisi lihtne ja arvutuslikult kiire.
- Soovitav on tehta loendur, mis “tiksub” nagu kell, et käivitada perioodiliselt andmefaili allalaadimine ja töötlemine
- Kui soovid kauakestvat ja täpsemat programmi, saad uuendamist rakendada ka väljuvatele sõidukitele
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
- Sorting algorithms visualized
https://www.toptal.com/developers/sorting-algorithms - Big-O cheat sheet
https://www.bigocheatsheet.com - Quicksort algorithm
https://en.wikipedia.org/wiki/Quicksort - Example structure
https://github.com/JackWetherell/c-project-structure - List of common C libraries
https://github.com/oz123/awesome-c

