Rdf-baasi ülesande soovitusi ja nõuandeid: erinevus redaktsioonide vahel

Allikas: Lambda
Mine navigeerimisribale Mine otsikasti
Resümee puudub
Resümee puudub
13. rida: 13. rida:
Soovitaksin teha tükid valmis näiteks sellises järjekorras:
Soovitaksin teha tükid valmis näiteks sellises järjekorras:


* Funktsioon, mis loeb select lausest välja failinimed ja tingimused ja paneb need vastavatesse massiividesse.
 
* Funktsioon, mis saab ette failinime, seepeale loeb sisse vastava csv faili ja tagastab kahemõõtmelise stringimassiivi, mille sees selle csv faili väljad (stringid) ridade kaupa.
* Programmeeri kõigepealt valmis faili massiivi lugemise funktsioon (tingimata tee selleks eraldi funktsioon!),   katseta, et käiks ok.
* Funktsioon, mis genereerib järjest kõik kombinatsioonid kõigi sisseloetud massiivide ridadest (täielik ristkorrutis).  
* Seejärel programmeeri valmis <b>String rdf_omabsumma(String failinimi, String id, String omadus, String vaartus)</b>,alustades lihtsamast juhust: tingimusi pole, uri-dega midagi ei viidata jne. Kui see lihtne variant käib, lisa  uri-dega tegutsemine ja lõpuks tingimused.
* Funktsioon, mis võtab ette ühe taolise ristkorrutise rea (stringimassiivi) ja kontrollib, kas see rida vastab where tingimustele select lauses (kasutades muidugi varem-ehitatud massiivi, kus where tingimused sees).  
* Seejärel programmeeri käsurea töötlus, mis lõpuks kutsub välja varem tehtud funktsiooni rdf_omabsumma.
* Pane tükid järjest kokku, kutsudes neid main funktsiooni sees järjest välja (kõigepealt select lause analüüs, siis tsükkel üle failinimede, kutsudes iga kord välja faili sisselugemise funktsiooni, siis ristkorrutise ehitamine, mis omakorda kutsub iga genereeritud rea jaoks välja where tingimuste kontrolli-funktsiooni.
* Nüüd tee valmis funktsioon <b>String rdf_kesomabmax(String failinimi, String omadus, String vaartus)</b>, mis jällegi kasutab varem tehtud funktsiooni  rdf_omabsumma.
* Lõpuks tee valmis käsurea-töötlus kesomabmax jaoks, kasutades vastuse leidmiseks muidugi funktsiooni rdf_kesomabmax
 


Mis juhtub, kui sa kõiki asju ei oska või ei jõua teha? Kui mingi mõistlik hulk komponente on tehtud ja töötavad, saad oma ülesande siiski arvestatud, ainult et väiksema arvu punktidega (kui praktikumijuhendaja hindab, et tehtud on pool tööd, siis saadki 5 punkti). Kui praktikumijuhendajale tundub, et tehtud on ikka väga vähe (kolmandik või vähem) siis ta üldjuhul seda ülesannet ei arvesta.
Mis juhtub, kui sa kõiki asju ei oska või ei jõua teha? Kui mingi mõistlik hulk komponente on tehtud ja töötavad, saad oma ülesande siiski arvestatud, ainult et väiksema arvu punktidega (kui praktikumijuhendaja hindab, et tehtud on pool tööd, siis saadki 5 punkti). Kui praktikumijuhendajale tundub, et tehtud on ikka väga vähe (kolmandik või vähem) siis ta üldjuhul seda ülesannet ei arvesta.


== Soovitusi  funktsioonide kirjutamisel ==
== Soovitusi  funktsioonide kirjutamisel ==
== uri-ga viidatud teiste failide lugemine ==
Kui oled esimese faili info sisse lugenud ja massiivi pannud, käi massiiv läbi, ning loe
lisaks (samasse tabelisse!) sisse kõik failid, mida uri-tüüpi väärtusega viidatakse. Ära
siis juba sisse loetud faili uuesti loe!


=== Globaalsed muutujad ===
=== Globaalsed muutujad ===
28. rida: 36. rida:
näiteks:
näiteks:


* from: ühemõõtmeline massiiv tabelitest/failidest select lause from osast
* readfiles: ühemõõtmeline massiiv, mis sisaldab kõigi sisseloetud failide nimesid
* where: kahemõõtmeline massiiv tingimustest select lause where osast
* rdfdata: kahemõõtmeline massiiv, mis sisaldab kõigi sisseloetud andmefailide infot korraga
* tables: kolmemõõtmeline massiiv sisseloetud tabelitest (iga tabel ise kahemõõtmeline massiiv)
* condname: tingimusvälja nimi
* resultheader: ühemõõtmeline massiiv tulemuse päisereaga
* condvalue: tingimusvälja väärtus
* resultrow: ühemõõtmeline massiiv tulemuse '''ühe reaga'''


Funktsioonid võivad vajadusel vabalt otse nende globaalsete muutujatega manipuleerida.
Funktsioonid võivad vajadusel vabalt otse nende globaalsete muutujatega manipuleerida.
41. rida: 48. rida:
Sinu programm peab päris mitmes kohas lammutama eraldajaga stringi tükkideks:
Sinu programm peab päris mitmes kohas lammutama eraldajaga stringi tükkideks:


* select lauses from (tabelite/failide) osa, eraldajaks komad: fail1, fail2, ...,failn
* kettalt loetud csv faili rida, eraldajaks komad
* select lauses where tingimused, eraldajaks '''and''': tulp1=vaartus1 and .... and tulpm=vaartusm
* käsurealt antud lause
* kettalt loetud csv faili rida, eraldajaks jällegi komad
* where lause iga üksik komponent tulp1=vaartus1, kus eraldajaks = ja komponente alati kaks.


Kuidas seda paremini teha?  
Kuidas seda paremini teha?  
55. rida: 60. rida:


Stringi lammutamiseks on palju meetodeid, kuid kõige lihtsam on uurida näiteprogrammi [[splitproov.java]] ja kasutada seal olevaid ideid. NB! Selleks, et selle programmi lõike kasutada, on vaja, et sa temast aru saaksid. Vaata kindlasti ka massiivide kasutamise näiteprogrammi [[massiivproov.java]]
Stringi lammutamiseks on palju meetodeid, kuid kõige lihtsam on uurida näiteprogrammi [[splitproov.java]] ja kasutada seal olevaid ideid. NB! Selleks, et selle programmi lõike kasutada, on vaja, et sa temast aru saaksid. Vaata kindlasti ka massiivide kasutamise näiteprogrammi [[massiivproov.java]]
=== where lauseosa tükkideks lõhkumine ===
where lauseosa (tulp1=vaartus1 and .... and tulpm=vaartusm) lammutamise tulemuseks
võiks olla kahemõõtmeline massiiv: m rida, kus igasühes kaks tulpa (tulba nimi ja vajalik väärtus). Selleks võid kõigepealt lammutada where lauseosa and-eraldaja järgi ühemõõtmeliseks massiiviks ja seejärel käia see massiiv tsükliga Üle, ja teha igast element-stringist omakorda kahe-elemendiline massiiv (eraldaja =). Nendest kahe-elemendilistest massiividest saadki kokku kahemõõtmelise massiivi kõigi vajalike stringidega:
tulp1  vaartus1
tulp2  vaartus2
...
tulpm  vaartusm
NB! Pane tähele, et kui väärtus-stringil on apostroofid ümber (näiteks 'koer') siis
ta tähistab konkreetset stringiväärtust, mis tulbas peab olema. Kui apostroofe pole,
siis tähistab väärtus-string teise tulba sisu! Seega pead apostroofid (kui nad on) kindlasti alles jätma, sest selle kaudu saad aru, et kumba variandiga on tegemist.


=== Failide sisselugemine ja komade järgi splittimine ===
=== Failide sisselugemine ja komade järgi splittimine ===
76. rida: 67. rida:
stringidest. Siis on lihtne seda funktsiooni tsüklis välja kutsuda, üle kõigi
stringidest. Siis on lihtne seda funktsiooni tsüklis välja kutsuda, üle kõigi
sisseloetavate failide.
sisseloetavate failide.
=== Failide/tabelite ristkorrutis ===
Vaata ülesande spetsifikatsioonist uuesti üle, mida selle all mõeldakse. Arusaamine on siinkohal hädavajalik!
Kuidas seda aga kirjutada?
Esiteks, pane tähele, et ülesande spetsifikatsioon '''ei nõua tulemus-massiivi koostamist, vaid ainult tulemuse väljatrükki'''! See lihtsustab veidi asja. Mõttekas on koostada ristkorrutist nii, et:
* Kõigepealt teed ühemõõtmelise stringimassiivi (nt nimega resultheader) tulemuse päisega (st kõigi failide/tabelite päiseread järjest, iga originaal-päisevälja ette lisatud failinimi ja punkt).
* Siis hakkad järjest koostama tulemusridu, iga kord ehitades üheainsa tulemusrea ühte ühemõõtmelisse massiivi (nt resultrow), mis seega saab pidevalt üle kirjutatud.
* Hiljem lisad siiakohta kontrolli select lause where osaga: et kas rida välja trükkida või ei: kui vastab tingimustele, trükime välja, muidu mitte.
* Esialgu ära where-tingimusi kontrolli, vaid trüki debugimise jaoks iga genereeritud rida välja.
Nüüd ristkorrutise juurde. On arusaadav, et tuleb teha tsükleid tsüklite sees, kusjuures igale tabelile/failile vastab üks tsükkel üle kogu faili. Kui meil oleks näiteks kaks tabelit, oleks  vaja teha umbes nii:
  for(i=0;i<tables[0][i].length;i++) {
    for(j=0;j<tables[1][j].length;ij++) {
      .... liida read tables[0][i] ja tables[1][j] .... 
    }
  }
Paraku ei saa me koodi aga kirjutada fikseeritud arvu tsükleid, sest sisseloetavate tabelite/failide arv sõltub kasutaja antud sisendist.
Mis siis teha? Üks võimalik meetod on järgmine. Olgu meil näiteks kolm massiivi,
st tables.length==3. Teeme ühe integer-abimassiivi tmp, samuti pikkusega kolm,
ja hakkame seal massiivis hoidma infot, et mitmendat rida massiivis i tuleks järgmisena kasutada. Kuna 0s rida on meil päiserida, ja kasutame alates reast 1 , siis tmp sisaldab
algul ühtesid.
Teeme nüüd nii:
* Ehitame uue tulemusrea, kasutades igast tabelist/failist just seda rida, millele vastav tmp väli viitab (esialgu kõik ühed).
* Nüüd suurendame esimest tmp elementi (st tmp[0]=tmp[0]+1;) ja kui ta läheb liiga suureks (tmp[0]>tables[0].length) siis teeme ta uuesti 1-ks (tmp[0]=1) ja suurendame hoopis järgmist tmp elementi (tmp[1]=tmp[1]+1;). Kui see ka liiga suur, teeme temagi üheks, ja läheme järgmise juurde (tmp[2]). Kui kõige viimane tmp element ka liiga suureks läheb, siis on kõik kombinatsioonid tehtud ja meie lõpetame. Muidu aga läheme tsüklis jälle eelmise sammu juurde ehitame uue tulemuse, kasutades tmp faili sisu kui viitajat kasutatavatele ridadele).
Kood oleks umbes selline:
  for(i=0;i<tmp.length;i++) tmp[i]=1; // algvaartused
  stopflag=false;
  for(;;) {
    // teeme uue rea
    for(i=0;i<tmp.length;i++) {
      ... kasuta rida tables[i][tmp[i]] ...
    }
    // muudame tmp sisu, et parast teha uus rida
    j=0;
    for(;;) {
      tmp[j]=tmp[j]+1; // votame jargmise rea
      if (tmp[j]<=tables[j].length) break; // koik ok, ei ole veel liiga suur vaartus     
      // muidu aga paneme yheks ja votame jargmise j (jargmise tabeli/faili)
      tmp[j]=1;
      j++;
      // kontrollime, kas oli akki viimane tmp element
      if (j>=tmp.length) {
        stopflag=true;  // peame valimisest tsyklist kah valjuma, st koik read tehtud!
        break;
      }       
    }
    if (stopflag) break;
  }
=== where tingimuse kontroll ===
Iga uue rea tegemise järel on sul:
* muutumatu tulemus-päiserea massiiv resultheader kõigi tabel.tulbanimi väärtustega
* värskelt tehtud üks tulemusrida massiivis resultrow
Nüüd tuleb teha tsükkel üle kahemõõtmelise where-osa massiivi. Iga elemendi where[i] juures:
* leida, mitmes element resultheader massiivis on sama, kui where[i][0] (tulbanimi tulp=vaartus paaris)
* oletame, et selle elemendi number oli N. Siis kontrollime ühte kahest (olenevalt, kas where[i][1] esimene sümbol oli apostroof või ei):
** kas "'"+resultrow[N]+"'" väärtus on sama, kui where[i][1]? Kasuta string1.equals(string2)!
** leiame tulba M resultheader massiivis, mis sisaldab stringi where[i][1] ja kontrollime, kas  resultrow[N].equals(resultrow[M])
== Kah kasulikke soovitusi ==




217. rida: 133. rida:
Elegantsema lahenduse saab kirjutada näiteks nii:  
Elegantsema lahenduse saab kirjutada näiteks nii:  


Esiteks loeme sisse esimese rea ja vaatame, mitu
Teeme esialgse, ajutise massiivi näiteks suurusega
tulpa seal on. Siis teame kohe massiivi teist
(tulpade arvu) dimensiooni. Järgmistes ridades
peab ju olema sama palju tulpasid.
 
Nüüd ei tea me ikkagi, mitu rida massiivis on.
 
Järgmisena teeme esialgse, ajutise massiivi näiteks suurusega
100 rida (ja õige arv tulpasid). Hakkame sinna
100 rida (ja õige arv tulpasid). Hakkame sinna
lugema. Kui fail mahtus ära, teeme uue massiivi,
lugema. Kui fail mahtus ära, teeme uue massiivi,

Redaktsioon: 27. september 2006, kell 13:44

Kuidas pihta hakata ja mis järjekorras programmeerida?

Põhimõte:

Hakka kirjutama valmis üksikuid juppe, alates lihtsamatest. Kui üks on valmis, testi ta läbi ja mine siis järgmise kallale. Testimiseks kirjuta trükkimisi vahele, hiljem kommenteeri mittevajalikud trükid välja või kustuta ära. Alles lõpuks pane kõik main-i sees kenasti kokku.

Väldi algul mistahes keerukusi ja lisavõimaluste tegemist! Ehita kõik algul valmis nii lihtsalt, kui saad, hiljem saad soovi korral koodi täiustada, kui aega jääb.

Soovitaksin teha tükid valmis näiteks sellises järjekorras:


  • Programmeeri kõigepealt valmis faili massiivi lugemise funktsioon (tingimata tee selleks eraldi funktsioon!), katseta, et käiks ok.
  • Seejärel programmeeri valmis String rdf_omabsumma(String failinimi, String id, String omadus, String vaartus),alustades lihtsamast juhust: tingimusi pole, uri-dega midagi ei viidata jne. Kui see lihtne variant käib, lisa uri-dega tegutsemine ja lõpuks tingimused.
  • Seejärel programmeeri käsurea töötlus, mis lõpuks kutsub välja varem tehtud funktsiooni rdf_omabsumma.
  • Nüüd tee valmis funktsioon String rdf_kesomabmax(String failinimi, String omadus, String vaartus), mis jällegi kasutab varem tehtud funktsiooni rdf_omabsumma.
  • Lõpuks tee valmis käsurea-töötlus kesomabmax jaoks, kasutades vastuse leidmiseks muidugi funktsiooni rdf_kesomabmax


Mis juhtub, kui sa kõiki asju ei oska või ei jõua teha? Kui mingi mõistlik hulk komponente on tehtud ja töötavad, saad oma ülesande siiski arvestatud, ainult et väiksema arvu punktidega (kui praktikumijuhendaja hindab, et tehtud on pool tööd, siis saadki 5 punkti). Kui praktikumijuhendajale tundub, et tehtud on ikka väga vähe (kolmandik või vähem) siis ta üldjuhul seda ülesannet ei arvesta.

Soovitusi funktsioonide kirjutamisel

uri-ga viidatud teiste failide lugemine

Kui oled esimese faili info sisse lugenud ja massiivi pannud, käi massiiv läbi, ning loe lisaks (samasse tabelisse!) sisse kõik failid, mida uri-tüüpi väärtusega viidatakse. Ära siis juba sisse loetud faili uuesti loe!

Globaalsed muutujad

Päris mõistlik on kasutada mingit hulka globaalseid muutujaid ja massiive, näiteks:

  • readfiles: ühemõõtmeline massiiv, mis sisaldab kõigi sisseloetud failide nimesid
  • rdfdata: kahemõõtmeline massiiv, mis sisaldab kõigi sisseloetud andmefailide infot korraga
  • condname: tingimusvälja nimi
  • condvalue: tingimusvälja väärtus

Funktsioonid võivad vajadusel vabalt otse nende globaalsete muutujatega manipuleerida.


Stringi lõhkumine eraldaja järgi

Sinu programm peab päris mitmes kohas lammutama eraldajaga stringi tükkideks:

  • kettalt loetud csv faili rida, eraldajaks komad
  • käsurealt antud lause

Kuidas seda paremini teha?

Esiteks, iga sellise lammutamise tulemuseks tasub anda stringimassiivi, kus elementideks on järjest vajalikud stringid. Hea on, kui see stringimassiiv teha kohe õige pikkusega, siis saab mh lihtsamalt kontrollida, kui palju elemente temas on (massiiv.length annab massiivi pikkuse).

Teiseks, mõistlik on kirjutada funktsioon, mis võtab ette lammutatava stringijupi, eraldaja-stringi (koma või and vÕi =) ja annab tagasi stringimassiivi.

Stringi lammutamiseks on palju meetodeid, kuid kõige lihtsam on uurida näiteprogrammi splitproov.java ja kasutada seal olevaid ideid. NB! Selleks, et selle programmi lõike kasutada, on vaja, et sa temast aru saaksid. Vaata kindlasti ka massiivide kasutamise näiteprogrammi massiivproov.java

Failide sisselugemine ja komade järgi splittimine

Sellest on veidi kirjas siinse jutu lõpuosas. Kindlasti tee funktsioon, mis saab ette failinime ja annab tagasi kahemõõtmelise massiivi temast leitud stringidest. Siis on lihtne seda funktsiooni tsüklis välja kutsuda, üle kõigi sisseloetavate failide.


Stringide võrdlemine

NB! Kui x ja y on stringid, siis võrdlemine

 x==y

ei anna oodatud tulemust! See võrdlus ainult kontrollib, kas x ja y on võrdsed pointerid, st viitavad samale stringile mälus. Aga, kui teeme nii:

 String x="ab";
 String y="ab";
 if (x==y) ...

siis x ja y on erinevad pointerid (kuigi stringide sisud on samad!) ja neid ei peeta võrdseks.

Selleks, et kontrollida, kas stringide sisud on samad, on vaja kasutada sellist spetsiaalset stringivõrdlust:

 x.equals(y)

(selle funktsiooni ja muud head stringifunktsioonid leiab nii Ecki õpikust kui ametliku manuaali lingilt http://java.sun.com/j2se/1.4.2/docs/api/java/lang/String.html)

Võrdlemaks, kas string x on suurem kui string y (vajalik sorteerimisel), kasuta järgmist funktsiooni:

 x.compareTo(y)

Lisainfot (mitte väga olulist) leiad eelnevalt viidatud linkidest.

Mida compareTo välja annab? int-i. Mida see int näitab? Katseta ise: võrdle paari stringi (pane katse main funktsiooni) ja trüki tulemus-int välja.


Kuidas teha õige suurusega massiiv, kui näiteks failist loed?

Failist lugemise kohta vaata kindlasti ka näiteprogrammi ioproov.java

Probleem selles, et meil on vaja panna failist loetud stringid massiivi, aga me ei tea ette, kui suur on fail. Seega, kui suur massiiv tuleks teha?

Siin on, nagu alati mitu lahendust.

Hästi lihtne ja ebaefektiivne lahendus on kõigepealt lugeda fail sisse, aga mitte massiivi panna: selle asemel lugeda kokku ridade arv (ja tulpade arv ka).

Siis teeme õige suurusega massiivi ja loeme seejärel failist uuesti kõik read, seekord pannes nad massiivi sisse.

Elegantsem lahendus on vältida mitmekordset lugemist. Aga: kui sa endas väga kindel ei ole, soovitan algul realiseerida lihtsama lahenduse, mida äsja kirjeldasin.

Elegantsema lahenduse saab kirjutada näiteks nii:

Teeme esialgse, ajutise massiivi näiteks suurusega 100 rida (ja õige arv tulpasid). Hakkame sinna lugema. Kui fail mahtus ära, teeme uue massiivi, seekord õige (lühema) pikkusega, ja kopeerime esialgse massiivi vajalikud read sinna massiivi. Vana massiiv visatakse mõne aja pärast prahikoristaja poolt automaatselt ära.

Kui fail ei mahtunud esialgsesse massiivi, teeme uue ajutise massiivi, seekord näiteks 2 korda suurema kui eelmine. Kopeerime vanast ajutisest kõik uude ajutisse ja jätkame failist uude massiivi lugemist. Võib juhtuda, et nüüd mahub ära. Siis lõpuks teeme õige pikkusega (lühema) massiivi.

Kui ikka ei mahtunud, kordame protseduuri: teeme jälle uue, viimasest masiivist jällegi kaks korda suurema ajutise masssiivi. Jne jne.

NB! Selliselt ajutisi massiive tehes pane tähele, et kuna tulpade arv on neis alati õige, ei ole tegelikult vaja kopeerida vanast massiivist uude üksikute stringide kaupa (st topelttsükliga). Kahemõõtmeline massiiv on ju massiiv mitmest ühemõõtmelisest! Kopeerimise käigus saame kopeerida korraga terve rea (käsitledes massiive kui ühemõõtmelisi) ja see on palju kiirem meetod. Siin on veel võimalik teha mitu täiendavat optimeeringut, aga liiale ei ole optimeerimis-pingutustega ka mõtet minna.