PR1ET6: Massiivid 2

Praktikumi materjal

Teooriakild 1: Teksti edastamine funktsiooni

Sageli võib tekkida olukord, kus funktsioon ise oleks justkui kenasti korduvkasutatav, kuid kasutajale kuvatav tekst ei sobi konkreetse kasutusjuhuga. See on tüüpiline üldiste sisend- ja väljundfunktsioonide puhul.

Üks võimalus selle lahendamiseks on tekst varem välja trükkida funktsioonis, mis teist funktsiooni välja kutsub. Täpselt nii oleme teinud viimase kahe nädala jooksul: väljastasime teksti funktsioonis   main() ja seejärel kutsusime välja teise funktsiooni. Üldiselt jääb see ka edaspidi soovituslikuks lähenemiseks. Sedasi on funktsioon lihtsam ja kergemini korduvkasutatav.

Alternatiivse lahendusena jälgime põhimõtet, kus erinevusi käsitletakse funktsiooni parameetrite abil – antud juhul on erinevuseks väljastatav tekst. Pea meeles: kui kopeerid ja kleebid koodi ainult väikese erinevuse pärast, proovi leida viis selle erinevuse lahendamiseks funktsiooni parameetrite abil. Seega anname erineva päiseteksti käsitlemiseks teksti funktsioonile parameetrina.

See töötab üsna hästi näiteks siis, kui tahame väärtuse (näiteks täisarvu) sisestamisel kuvada kasutajale juhise. Samuti võib see hästi sobida massiivi väljastamiseks nii, et selle ees kuvatakse kirjeldav tekst.

NB! Kaks olulist mõtet veel! Mõlemast neist räägime süviti semestri teises pooles.

  1. Tekst on massiiv, mis koosneb üksikutest tähemärkidest, mida saab vajadusel indekseerida (hilisem teema)
  2. Erinevalt arvumassiividest ei pea me tekstimassiivi väljastamiseks selle pikkust funktsioonile kaasa andma, sest kasutatakse nullmärgiga lõpetatud sõnesid (zero-terminated strings), kus viimase kuvatava märgi järel asub spetsiaalne nullmärk. Ka see on hilisem teema.

Teooriakild 2: Massiivi vs massiivi liikme edastamine

Sellega tutvusime küll eelmises tunnis, kuid tuletame meelde. Kõigepealt mõned põhipunktid

  1. (Staatilise) massiivi deklaratsioonil on fikseeritud suurus, mis peab olema määratud juba deklareerimise ajal. Massiivi suurust ei saa programmi töö ajal muuta (nt suurendada)
  2. Massiiv edastatakse alati viitena originaal asukohale
  3. Massiivi sisu saab muuta alamfunktsioonides, muudatus kehtib kõikjal
  4. Funktsioonile edastatud massiivi pikkust ei saa funktsiooni sees automaatselt kindlaks teha, seega peame alati edastama ka massiivi pikkuse
  5. Funktsiooni sees lokaalselt deklareeritud massiivi ei saa funktsioonist tagastada

See tähendab, et praegu deklareerime massiivid peaaegu alati main()  funktsioonis ja anname need teistele funktsioonidele parameetritena edasi.

Teiseks tuletame meelde, kuidas kantsulud töötavad

  1. Kui massiivi ees on andmetüüp, tähistab see massiivi deklareerimist ja massiivi suurus tuleb määrata (nt int array[10]; )
  2. Kui andmetüüp puudub, tähistab see massiivi konkreetse elemendi indekseerimist (nt array[3] )
  3. Ühemõõtmelise massiivi puhul võib suuruse ära jätta ainult siis, kui
    1. massiiv initsialiseeritakse samal ajal (nt  int array[] = {1, 2, 3, 2 ,1}; )
    2. massiivi kasutatakse funktsiooni parameetrina (nt  int Foo(int arr[], int n); )

See viib meid järgmise näiteni, kus võrdleme, kas funktsioonile antakse edasi terve massiivi või ainult üks massiivi element.

Teooriakild 3: (varajane) tagastamine void-tüüpi funktsioonist

Kuigi oleme öelnud, et void  tüüpi funktsioonid ei tagasta väärtust, ei tähenda see, et nende täitmist ei saaks varem lõpetada. void  tüüpi funktsioonist väljumiseks saab kasutada käsku returnilma ühegi väärtuseta.

Seda kasutatakse sageli sisendparameetrite kontrollimisel, et teha kindlaks, kas funktsioon saab oma tööd jätkata. Näiteks vaatame funktsiooni, mis väljastab massiivi. Kui midagi ei ole väljastada (nt massiiv on tühi), tuleks funktsiooni töö lõpetada enne tsükli käivitamist.

Sel nädalal on sellest teadmisest kasu peamiselt lisaülesande lahendamisel. Tulevikus on seda teadmist vaja ka põhiülesannete lahendamisel.

Esitamisele kuuluvad ülesanded

Selles tunnis tuleb lahendada kaks ülesannet. Esimest ülesannet laiendab kaks lisaülesannet.

Ülesanne 1 [W06-1]: Uute massiivide koostamine

Antud ülesanne põhineb suuresti algoritmiülesandel. Vastav ülesanne on püstitatud siin:  https://blue.pri.ee/ttu/programmeerimine-i/algoritmide-ulesanded/#Algoritm_3_Negatiivsete_ja_positiivsete_arvude_umberjarjestus

Selle algoritmi lahendus on sulle ette antud. Ülesandele on lisatud algoritmis puuduvaid täiendusi.

Algoritm

Pane tähele, et tulemusmassiivi loomine toimub kahe järjestikuse iteratsiooni vältel, et säilitada algset esinemisjärjekorda. Esitatud joonistel on täitmata lahtrid tähistatud küsimärgiga. Selle eesmärk on ühtlasi meenutada, et algväärtustamata massiivi liikmed on juhuslikud väärtused. Pärast esimest iteratsiooni peaks seis olema järgnev:

Oluline: Pööra tähelepanu mõlema massiivi indekseerimisele!!

Teise iteratsiooni lõpuks täidetakse kogu vastusmassiiv väärtustega.

Nõuded
  • Programm peab põhinema etteantud algoritmil
  • Kasutajalt loetakse 6 arvu ja salvestatakse need massiivi
  • Programmi vältel koostatakse teine samapikk massiiv põhimõttel negatiivsed arvud ettepoole, positiivsed nende järel. Arvu 0 loetakse positiivseks arvuks
  • Programmi vältel koostatakse ka kolmas massiiv, kuhu kuuluvad vaid nullist suuremad arvud. Massiivi pikkus võib osutuda lühemaks algsest massiivist
  • Pärast massiivide koostamist väljastatakse järjest kõik 3 massiivi
  • Programm peab endas sisaldama kokku nelja funktsiooni – arvude lugemiseks kasutajalt, arvude ekraanile väljastamiseks, nullist suurematest arvudest koosneva massiivi koostamiseks ning etteantud algoritmi alusel järjestatud massiivi koostamiseks.
  • Kõik enda loodud funktsioonid peavad olema välja kutsutud main()  funktsioonist.
Soovitusi ja vihjeid

Alusta programmi koostamist sisestusest ja massiivi ekraanile kuvamisest. Seda oled juba eelmine kord teinud. Loo vajalik massiiv ja korduvkasuta varasemalt tehtud funktsioone. Kui kopeerid  funktsioone eelneva nädala ülesandest, ära unusta ka nendele eelnevat kommentaari kopeerida!

Nüüd lähme ümberjärjestamise juurde. Loo uus massiiv (lisaks olemasolevale), kuhu salvestad ümberjärjestatud arvujada. Uue massiivi pikkus on sama nagu esmase massiivi pikkus. Meenuta, et funktsioonist massiivi tagastada ei ole võimalik, seega massiiv tuleb deklareerida  main() funktsioonis ja edastada funktsioonile. Ümberjärjestamise ülesande lahendamiseks jälgi etteantud algoritmi, mis oli esitatud veidi ülevalpool.

Seejärel kutsu uuesti välja massiivi väljastamise funktsioon, sel korral ümberjärjestatud massiivi väljastamiseks. St kood võiks funktsiooni väljakutsete mõttes näha välja umbes sarnane (nimed ei ole olulised):

Siinkohal kui soovid oma main()  funktsiooni puhtamaks saada, siis tasuks massiivi väljastuse ette minev tekst parameetriga kaasa anda. Selleks vaata esimest teooriakildu!

Nüüd liigu viimase funktsiooni juurde, mis peaks ainult nullist suurematest arvudest massiivi koostama. Struktuurilt on koostamine üsna sarnane eelnevalt tehtud ümberjärjestusele, kuid mõned erinevused siiski eksisteerivad. Kui ümberjärjestatud massiiv oli liikmete arvult samapikk algse massiiviga, siis nullist suuremate arvude massiiv tuleb suure tõenäosusega lühem. Mõtle, kuidas saada koostatud massiivi pikkus alamfunktsioonist välja,  main()  funktsiooni! Kui pikkus käes, kutsu jälle main()  funktsioonis välja massiivi väljatrükk, et ka kolmas massiiv ekraanile väljastada.

Testimine

Esimese testina proovime tavapärast juhtu, kui sisestuses on nii positiivseid kui negatiivseid arve.

Arvesta, et antud olukorras võivad ka nullist suuremad arvud puududa. Väljastus peaks jääma jätkuvalt arusaadavaks.

Ülesanne 2 [W06-2]: kahe arvu võrdlus

Teises ülesandes praktiseerime massiivi indekseerimist, pöördudes vaid soovitud massiivi indeksite poole ning teostame lihtsaid operatsioone leitud väärtustega.

Nõuded
  • Loe klaviatuurilt 5 täisarvu
  • Küsi kasutajalt kahe soovitud arvu positsioon (järjekorranumber)
    • Kontrolli, et need oleksid sobilikus vahemikus enne jätkamist! Vajadusel küsi uuesti
    • Numeratsioon peab olema sama nagu sisendit küsides
    • Soovitus: kontrollimaks kas leidsid õiged arvud, trüki need välja
  • Leia sisestatud positsioonide põhjal nendele vastavad arvud algsest massiivist. Kasutades neid arve tee järgnevat:
    • Võrdle neid kahte arvu ning kuva nende omavaheline suhe (väiksem, suurem, võrdne)
    • Koosta jagamistehe ning kuva selle vastus. Tehe tuleb koostada sedasi, et suurem arv on jagatav ning väiksem arv on jagaja.
    • Vastus anna ühe komakohaga
  • Ülesandes on vaja luua neli funktsiooni. Funktsioonid on sulle vähemal või rohkemal määral kirjeldatud järgnevas peatükis.
Loodavad funktsioonid

1. Massiivi lugemine. Selleks korduvkasuta koodi varasematest programmidest. Kopeeri funktsioon koos sellele eelneva kommentaariga.

2. Indeksi lugemine. Selleks tuleks luua uus funktsioon. Loome korduvkasutatava funktsiooni, mida saad tulevikus kasutada igal korral, kui on vaja kasutajalt lugeda täisarv kindlas vahemikus (nt 0 … 5, -10 … 10 jne). Kui tegid eelmine nädal lisaülesande ära, on sul see funktsioon suure tõenäosusega juba olemas. 

Pakun välja kaks funktsiooni varianti, vali kumb meelepärasem tundub ja realiseeri see.

Esimene versioon eeldab, et kasutajale esitatav küsimus küsitakse väljaspool seda funktsiooni.

Teine versioon eeldab, et küsimus saadetakse funktsiooni kaasa ja väljastatakse selle sees. Vaata esimest teooriakildu.

3. Arvude võrdlemine. Siia funktsiooni tuleks edastada 2 arvu, mida võrreldakse omavahel, ning väljastatakse kumb neist oli suurem (või olid nad võrdsed). Väljastus toimub funktsiooni sees. Väljastus on kujul “a < b”, “a > b” või “a = b”

NB! Arvude edastamiseks siia funktsiooni vaata teist infokildu!

4. Jagatise teostamine.  Siia funktsiooni edastatakse samamoodi kaks arvu. Taaskord toimub arvude võrdlemine nagu ka eelmine kord, kuid sel korral määrab võrdluse tulemus jagamistehte operandide järjekorra.

Testimine

NB! Näites on nähtavale jäetud silumiseks kasutatavad read (algusega DEBUG), mida soovitan ka sinul alguses teha. Need saad hiljem kiirelt välja kommenteerida.

Testid

Järgnevalt pakun välja osalise loetelu testidest, mida proovida

  • Proovi koodi sisenditega, mis peaksid andma tulemuse ilma ühegi veata
  • Proovi indekseid, mis on massiivi piiridest väljas.  Katseta seda nii esimese kui teise sisendiga.
  • Proovi massiivi piiridest välja jäävaid indekseid korduvalt, et programm ei läheks edasi enne, kui sobilik sisend on saadud.
  • Proovi piirjuhtusid
    • Kui loendasid 0 – 4, proovi -1, 0, 4, 5
    • Kui loendasid 1 – 5, proovi 0, 1, 5, 6
  • Proovi sisendeid, mis testiksid läbi kõik 3 võrdlusjuhtu (<, >, ==)
  • Proovi nulliga jagamist. NB! Nulliga jagamine on defineerimata operatsioon. Nulliga jagamine võib jooksutada programmi või isegi kogu arvuti kokku (mikrokontrollerite puhul on tavapärane, et terve seade taaskäivitab end nulliga jagamise tulemusel). Nulliga jagamistehet ei tohi sooritada!

Lisaülesanne 1 [W06-3]: Ühekordsed arvud

Ülesanne on tunnitöö #1 laiendus.

Nõuded
  • Ülesanne peab olema tehtud laiendusena baasülesandele. Baasülesande funktsionaalsus peab säilima täies ulatuses.
  • Loo uus massiiv olemasolevate arvude põhjal. Uus massiiv tuleb luua põhimõttel, et kõik numbrid tohivad esineda vaid ühekordselt.
Näide lahendusest

Lisaülesanne 2 [W06-4]: Vastandarvude paarid

Ülesanne on tunnitöö #1 laiendus.

Nõuded
  • Ülesanne peab olema tehtud laiendusena baasülesandele. Baasülesande funktsionaalsus peab säilima täies ulatuses.
  • Leia ja kuva massiivis olevate arvude ja nende vastandarvude paarid.
  • Samu arve tohib paarina kuvada ühekordselt, olenemata arvude järjestusest või korduste arvust.
Testimine

Testimisel veendu, et paarid ei korduks.

Pärast tundi peaksid

  • Oskama sõnesid (teksti) funktsioonidesse edastada
  • Oskama void-tüüpi funktsioonidest väljuda varakult return lausega
  • Oskama funktsiooni edastada nii terviklikku massiivi kui ka üksikut massiivi liiget
  • Oskama koostada ning kasutada massiive, mille kõik liikmed (positsioonid) ei ole kasutusel
  • Tundma end mugavamalt massiivi käsitledes ka juhtudel, kus läbikäik pole esimesest elemendist viimaseni
  • Oskama kopeerida ühest massiivist arve teise massiivi, kusjuures kasutades kahte erinevat indeksit ühes samas tsüklis erinevate massiivide indekseerimiseks.
  • Tundma ennast juba natuke kodusemalt funktsioone kasutades