olen nyt tehnyt tämmöisen laskimen, mutta se näyttää kerto- ja jakolaskuissa tekevän se laskun vain kerran. esim. 3/3/3/3 = 1.0? ja 3*3*3*3 = 9..
public class Laskin { /******************************************************************************************/ public static void plussa() { System.out.println("\nPlus-laskenta 0 lopettaa\n========================="); int i = 0; double summa = 0; while ( i == 0 ) { double luku = Lue.dluku(); summa += luku; if (luku == 0) { System.out.println("Lopetit plussaamisen"); i = 1; } } System.out.println("Lukujen summa on: "+summa); } /******************************************************************************************/ public static void miinus() { System.out.println("\nMiinus-laskenta 0 lopettaa\n==========================="); int i = 0; double alkuluku = Lue.dluku(); double erotus = 0; while ( i == 0 ) { double luku = Lue.dluku(); erotus = alkuluku -= luku; if (luku == 0) { System.out.println("Lopetit miinustamisen"); i = 1; break; } } System.out.println("Lukujen erotus on: "+erotus); } /******************************************************************************************/ public static void jako() { System.out.println("\nJako-laskenta 0 lopettaa\n========================="); int i = 0; double alkuluku = Lue.dluku(); if ( alkuluku == 0) { System.out.println("Lopetit Jakamisen"); i = 1; } double osamaara = 0; while ( i == 0 ) { double luku = Lue.dluku(); if (luku == 0) { System.out.println("Lopetit Jakamisen"); i = 1; break; } else { osamaara = alkuluku / luku; } } System.out.println("Lukujen osamäärä on: "+osamaara); } /******************************************************************************************/ public static void potenssi() { System.out.println("\nPotenssi 0 lopettaa\n===================="); int i = 0; double tulos = 0; while (i == 0) { System.out.println("Syötä kantaluku"); double f = Lue.dluku(); if (f == 0) { System.out.println("Lopetit potenssilaskennan"); i = 1; break; } System.out.println("Syötä eksponentti (0.5 tarkoittaa neliöjuurta)"); double p = Lue.dluku(); if (p == 0) { System.out.println("Lopetit potenssilaskennan"); i = 1; break; } else { tulos = Math.pow(f, p); System.out.println("Lukujen tulos on: "+tulos); } } } /******************************************************************************************/ public static void kerto() { System.out.println("\nKerto-laskenta 0 lopettaa\n=========================="); int i = 0; double alkuluku = Lue.dluku(); if ( alkuluku == 0) { System.out.println("Lopetit Kertomisen"); i = 1; } double tulo = 0; while ( i == 0 ) { double luku = Lue.dluku(); if (luku == 0) { System.out.println("Lopetit Kertomisen"); i = 1; break; } else { tulo = alkuluku * luku; } } System.out.println("Lukujen tulo on: "+tulo); } /******************************************************************************************/ public static void main(String[] args) { int komento; do { System.out.println("\n\nLaskin - 0 lopettaa\n==================\n\n1 - Plus-laskenta\n\n2 - Miinus-laskenta\n\n3 - jakolasku\n\n4 - Kertolaskenta\n\n5 - Potenssilaskenta\n"); komento = Lue.kluku(); switch (komento) { case 1: plussa(); break; case 2: miinus(); break; case 3: jako(); break; case 4: kerto(); break; case 5: potenssi(); break; case 0: System.out.println("Lopetit laskimen"); break; default: System.out.println("Virhe syötteessä"); break; } } while (komento != 0); } }
osaako kukaan neuvoa?
Edit: Käytän siis Javaa
osamaara = alkuluku / luku;
Jaat joka kerta ensimmäisen luvun juuri syötetyllä. Välissä olevilla ei siis ole merkitystä, vaan 6 / 123 / 456 / 789 / 2 = 3, koska 6 / 2 = 3.
Ihan oikeasti voisit opetella lukemaan itse ajatuksella koodiasi. Ilman sitä taitoa et koskaan pääse kovin pitkälle.
tiesin kyllä mikä ongelma on, mutta en osannut korjata sitä...
Jaa tuolla mainitsemallani rivillä ensimmäisen luvun sijaan aina välitulos. Ennen silmukkaa sinun täytyy laittaa "välitulokseksi" alkuluku.
Sama virhe ja korjaus koskevat myös kertolaskua.
Edit: saanko hieman selvemmän esimerkin, kun itse kokeilin tajuamallani tavalla, niin se meni väärin.
Ota kynä ja paperia. Tee paperille taulukko, jossa on sarakkeet "alkuluku", "luku" ja "osamaara". Käy läpi funktiotasi kuvitteellisella syötteellä (vaikka 200 / 2 / 5 / 10). Lue aina yksi rivi koodia ja kirjoita paperille, mitkä ovat muuttujien arvot tämän koodirivin jälkeen. Jos jonkin rivin jälkeinen tilanne vähänkin mietityttää, ota Java-kirja käteen ja lue uudestaan.
Siinä vielä korjattu silmukka:
osamaara = Lue.dluku(); while (true) { jakaja = Lue.dluku(); if (jakaja == 0) { break; } osamaara = osamaara / jakaja; }
ei toimi vieläkään.. kokeilin kahdella eri tavalla, toinen tuo ylempi..
Edit: voisko tuohon laittaa ton while loopin toisen while loopin sisään ja laittaa sellaisen välivastauksen?
Edit: sainkin toimimaan ihmeen kaupalla :D
Ei ohjelmointi ole mikään tuurilaji.
se on vain sanonta... :P
Hyvä Tommittaja.
Kannataa tosiaan kyllä opetella käymään sitä oman koodin toimintaa läpi paperilla ja/tai päässä, jotta osaa sitten etsiä sieltä, että missä se vika voisi olla. Tässäkin ilmeisesti olisit voinut ongelman tajuta helposti itsekin, jos olisit ensiksi vaikka kokeillut erilaisia laskutoimituksia. Kun syötät aina erilaisia syötteitä, niin jossain vaiheessa varmaan huomaat, että missä mahdollisesti menee vikaan tai saat jonkinlaisen johtolankan virheen syystä. Sitten on helmpompi sieltä koodista lähteä sitä virhettä metsästämään.
Asiaa voisi ehkä auttaa, että luet myös toisten kirjoittamaa koodia. Koodin lukeminen ja kirjoittaminen ovat ihan eri taitoja, kumpikin tärkeitä. Putkastakin voit löytää melko yksinkertaisia koodivinkkejä, jotka varmasti pystyt tajuamaan jo osaamiesi asioiden avulla.
Suosittelisin myös panostamaan koodin ulkoasuun hieman, nyt sisennykset ovat välillä miten sattuu (while lauseiden sisennys erilaista eri metodeissa) ja muuttujien nimet huonosti muuttujaa kuvaavia. Return lauseen merkitys ei liene ihan vielä sinulle selvinnyt, sillä tuota koodia saisi mielestäni paljon paremmaksi sen avulla.
Kyllä sinusta varmasti vielä ohjelmoija tulee, jos vain jaksat opetella asioita. Ohjelmointiuran alussa varmasti monet asiat tuntuvat vaikeilta, koska ne ovat täysin vieraita ja välttämättä samanlaisia ajattelumalleja ei ole tullut vastaan aiemmin.
Kirjoittelimpa tässä ajankulukseni sitten tuota Tommittajan koodia uusiksi, sellaiseen muotoon, millaisena itse näkisin sen paremmin muotoiltuna. Helppoahan se on kerta kirjoitella tänne viestiä, jossa kertoo, että voisi tuota muotoilua, rakennetta ja ties mitä parantaa. Tässä sitten tulee myös jotain konkreettisia esimerkkejä tuon koodin parantamiseksi.
import java.util.Scanner; public class Laskin { public static Scanner n = new Scanner(System.in); public static double lueDouble(){ Double d = 0.0; String rivi; while(true){ System.out.print("Syötä luku >"); rivi = n.nextLine(); try { d = Double.parseDouble(rivi); break; } catch (NumberFormatException e) { System.out.println("Ei ollut luku"); } } return d; } public static void kerto() { System.out.println("\nKerto-laskenta 0 lopettaa\n=========================="); int i = 0; double alkuluku = lueDouble(); if ( alkuluku == 0) { System.out.println("Lopetit Kertomisen"); i = 1; } double tulo = 0; while ( i == 0 ) { double luku = lueDouble(); if (luku == 0) { System.out.println("Lopetit Kertomisen"); i = 1; break; } else { tulo = alkuluku * luku; } } System.out.println("Lukujen tulo on: "+tulo); } public static void hiemanParempiKerto(){ System.out.println("\nKerto-laskenta 0 lopettaa\n=========================="); double tulo = 0; double alkuluku = lueDouble(); if(alkuluku == 0){ System.out.println("Lopetit kertomisen"); System.out.println("Lukujen tulo on: "+tulo); return; } double luku = 0; tulo = alkuluku; while(true){ luku = lueDouble(); if(luku == 0){ System.out.println("Lopetit kertomisen"); break; } else { tulo = tulo * luku; } } System.out.println("Lukujen tulo on: "+tulo); } public static void parempiKerto(){ System.out.println("\nKerto-laskenta 0 lopettaa\n=========================="); double tulo = 1.0; double luku = 0.0; int n = 0; while(true){ luku = lueDouble(); if(luku == 0) { break; } else { tulo *= luku; n++; } } System.out.println("Lopetit Kertomisen"); if(n <= 1){ System.out.println("Et syöttänyt tarpeeksi lukuja, joten tuloa ei voida laskea."); } else { System.out.println("Lukujen tulo on: "+tulo); } } public static void main(String args[]){ parempiKerto(); } }
Tuossa on siis alkuperäinen Tommittajan kerto -metodi (joka ei siis toimi oikein), itse kirjottamani hiemanParempiKerto- ja parempiKerto -metodit, lyhyt main -metodi. Lisäksi piti kirjoittaa oma double -lukuja käyttjältä lukeva metodi.
hiemanParempiKerto -metodin ajatuksena oli kirjoittaa tuo Tommittajan kerto -metodi mielestäni paremmin muotoiltuna ja selkeämpänä rakenteena. Ja myös toimivana versiona. Poistin kokonaan muuttujan i, koska se voitiin korvata lisäämällä return -kutsu muuttujan alkuluku kysymisen jälkeen tulevaan if-lohkoon ja while lohkossa se on oikeasti tarpeeton.
Samat tulostuskutsut ovat metodissa kahteen kertaan, joten ainakin minun intuitioni sanoo, että homman voi tehdä paremminkin. Sama intuitio kielii parannuksen mahdollisuudesta myös sen takia, että nyt joudutaan ensin lukemaan yksi luku ja testaamaan sitä, jonka jälkeen myöhemmin luetaan lisää lukuja ja testataan niitä täsmälleen samalla ehdolla. Lisäksi minusta on hieman epämääräistä sanoa lukujen tuloksi 0, jos käyttäjä ei syötä yhtään lukua. Tai että tulo olisi alkuluvun arvo, jos käyttäjä syöttää vain yhden luvun. Joten eikun kirjoittamaan parempaa versiota.
parempiKerto -metodi sitten on tuo parempi versio. Nyt siinä kaikki luvut luettaan samassa silmukassa. Uutena asiana on mukana laskuri lukujen määrän muistamiseen. Nyt, jos käyttäjä syöttää alle kaksi lukua, voidaan ilmoittaa helposti silmukan jälkeen, että tuloa ei ole määritelty. Lisäksi siinä ei toistu samat tulostuskutsut useassa kohtaa. Muutenkin minusta toteutus on huomattavasti paremmin ymmärrettävissä.
Tuon parempiKerto -metodin idean myös keksii helposti, jos kirjoittaa kynällä mitä ollaan tekemässä. Tuon voisi kirjoittaa esimerkiksi seuraavin vaihein:
1. Luetaan käyttäjältä luku.
- Jos luku oli nolla, niin siirrytään kohtaan 2.
- Jos luku ei ollut nolla, niin kerrotaan tulon arvoa ylläpitävä muuttuja luetulla luvulla. Toistetaan vaihe 1.
2. Tarkistetaan luettujen lukujen määrä ennen nollaa.
- Jos lukuja oli alle kaksi, niin kerrotaan että tuloa ei voi laskea.
- Jos lukuja oli enemmän, niin tulosteaan saatu tulo.
Tuosta nyt suoraan näkee, että selviää yhdellä while -silmukalla, jonka sisässä on if-lohko kertomassa milloin silmukan pitää päättyä. Lisäksi nähdään heti, että tarvitaan muuttuja lukujen määrää varten, tuloa varten ja luettavaa lukua varten. Ainut mikä ei ehkä ihan suoraan näy, että tuloa ylläpitävä muuttuja pitää alustaa luvulla 1. Senkin kyllä keksii helposti. Tämän jälkeen koodin kirjoittamisen pitäisi olla melko helppo tehtävä.
Nyt voisi sitten kirjoittaa tuon alkuperäisen kerto -metodin idean tähän eri vaihein, mutta sen voi jokainen itsekseen tehdä. Veikkaan, että siitä tulee huomattavasti monimutkaisempi kuin nuo minun kuvailemani vaiheet.
Toivottavasti tämä osoittaa, edes jollain tapaa mm. miksi sitä kynää ja paperia kannattaa käyttää. Varsinkin ohjelmointitaipaleen alussa siitä on huomattavasti apua ja kyllä myöhemminkin. Jospa tästä nyt oli hyötyä edes jollekulle tai herätti jotain ajatuksia jonkun päässä.
Ja tosiaan nämä ovat siis vain omia näkemyksiäni asiasta, eri mieltä sopii olla ja tuokaa se esille perusteluiden kera. Hieman väsyneenä tämän kirjoitin, mutta toivottavasti ei virheitä nyt ajatuksen kulussa sattunut.
sain sen tosiaan toimimaan näin:
public class Laskin { /*************************************************************************************************************/ public static void plussa() { System.out.println("\nPlus-laskenta 0 lopettaa\n========================="); int i = 0; double summa = 0; while ( i == 0 ) { double luku = Lue.dluku(); summa += luku; if (luku == 0) { System.out.println("Lopetit plussaamisen"); i = 1; } } System.out.println("Lukujen summa on: "+summa); } /*************************************************************************************************************/ public static void miinus() { System.out.println("\nMiinus-laskenta 0 lopettaa\n==========================="); int i = 0; double alkuluku = Lue.dluku(); if (alkuluku == 0) { System.out.println("Lopetit miinustamisen"); i = 1; } double erotus = 0; while ( i == 0 ) { double luku = Lue.dluku(); erotus = alkuluku -= luku; if (luku == 0) { System.out.println("Lopetit miinustamisen"); i = 1; break; } } System.out.println("Lukujen erotus on: "+erotus); } /*************************************************************************************************************/ public static void jako() { System.out.println("\nJako-laskenta 0 lopettaa\n========================="); int i = 0; double osamaara = Lue.dluku(); if (osamaara == 0) { System.out.println("Lopetit jakamisen"); i = 1; } while ( i == 0 ) { double luku = Lue.dluku(); if (luku == 0) { System.out.println("Lopetit Jakamisen"); break; } else { osamaara /= luku; } } System.out.println("Lukujen osamäärä on: "+osamaara); } /*************************************************************************************************************/ public static void kerto() { System.out.println("\nKerto-laskenta 0 lopettaa\n=========================="); int i = 0; double tulo = Lue.dluku(); if (tulo == 0) { System.out.println("Lopetit Kertomisen"); i = 1; } while ( i == 0 ) { double luku = Lue.dluku(); if (luku == 0) { System.out.println("Lopetit Kertomisen"); break; } else { tulo *= luku; } } System.out.println("Lukujen tulo on: "+tulo); } /*************************************************************************************************************/ public static void potenssi() { System.out.println("\nPotenssi 0 lopettaa\n===================="); int i = 0; double tulos = 0; while (i == 0) { System.out.println("Syötä kantaluku"); double f = Lue.dluku(); if (f == 0) { System.out.println("Lopetit potenssilaskennan"); i = 1; break; } System.out.println("Syötä eksponentti (0.5 tarkoittaa neliöjuurta)"); double p = Lue.dluku(); if (p == 0) { System.out.println("Lopetit potenssilaskennan"); i = 1; break; } else { tulos = Math.pow(f, p); System.out.println("Lukujen tulos on: "+tulos); } } } /*************************************************************************************************************/ public static void main(String[] args) { int komento; do { System.out.println("\n\nLaskin - 0 lopettaa\n===================\n\n1 - Plus-laskenta\n\n2 - Miinus-laskenta\n\n3 - jakolasku\n\n4 - Kertolaskenta\n\n5 - Potenssilaskenta\n"); komento = Lue.kluku(); switch (komento) { case 1: plussa(); break; case 2: miinus(); break; case 3: jako(); break; case 4: kerto(); break; case 5: potenssi(); break; case 0: System.out.println("Lopetit laskimen"); break; default: System.out.println("Virhe syötteessä"); break; } } while (komento != 0); } }
mutta kyllä, en tajua tuota return- juttua ollenkaan, mitä muka tarkoittaa että return "palauttaa arvonaan" jonkin luvun/muuttujan?? sitten on vielä taulukot, joita en ole kunnolla oppinut..
Edit: tuota Lue- luokkaa ei olisi tarvinnut kirjoittaa uudestaan. sen saa helposti osoitteesta http://www.cs.helsinki.fi/u/lppelton/java/Lue.java
Edit2: miksi kaikki luulevat, että en tajunnut tuota ongelmaa; sanoin selvästi, että tajusin ongelman, mutta en osannut korjata sitä...
Suosittelen lukemaan sitä kirjaa, mikä sinulla ja käymään tarkasti läpi sen esimerkit. Jos kija on aloittelijoille tarkoitettu, niin siinä on jopa rivi riviltä selostettu aina esimerkkikoodien toiminta. Käy niitä läpi.
Return ei palauta muuttujaa, vaan se voi palauttaa muuttujan arvon (int, double, String, viittaus johonkin luokkaan, jne.). Jos katsot tuota koodia jonka tuohon aiempaan viestiini kirjoitin, niin huomaat että lueDouble -metodissa käytetään returnia palauttamaan metodissa luetun luvun arvo (älä välitä jos ihan kaikkea muuta siitä ymmärrä). Yhtälailla sinun käyttämäsi Lue.dluku -metodi käyttää return lauseketta palauttamaan lukemansa arvon.
Jos taas katsot kirjoittamaani hiemanParempiKerto(), niin sieltäkin löytyy return lause. Koska metodin palautusarvo on tyyppiä void, eli se ei palauta mitään, niin return vain päättää sen metodin koodin suorittamisen ja ohjelma palaa suorittamaan koodia sinne mistä metodia kutsuttiin.
Tässä vielä ihan yksinkertainen esimerkki asiasta.
public class Return { public static int palautaKolme(){ int i = 3; return i; // Palautetaan muuttujan i arvo, joka on 3. "return 3" tekisi saman asian myös. } public static void main(String args[]){ int luku = 0; luku = palautaKolme(); System.out.println("Nyt luku on: " + luku); } }
Tuon ei pitäisi olla ylivoimainen ymmärtää. Nyt voit itse kokeilla tehdä erilaisia metodeita ja kokeilla erilaisia return lauseita. Nopeasti tajuat miten se toimii, ainakin toivon niin.
Toivottavasti ymmärsit mitä halusin tuolla aiemmalla viestilläni sanoa.
PS. En kirjoittanut Lue -luokkaa uusiksi. Tein vain oman metodin lukujen lukemiseen käyttäjältä, ei mennyt kauaa.
mjoo.. tajusin tuon suurinpiirtein, ainoa asia mitä en ymmärtänyt, oli mitä hyötyä on palauttaa joku arvo?
Edit: käänsin tuon koodin ja nyt tajuan paljon paremmin.. olen pitkään ihmetellyt tuota.. :D
Edit2: voiko laittaa näin?
public static int palautaI() { int i = Lue.rivi(); return i; } public static void main(String[] args) { int luku = palautaI(); }
Edit3: Tuo void- tyyppisessä lauseessa oleva return siis vain lopettaa metodin suorituksen?
Tommittaja kirjoitti:
Edit2: voiko laittaa näin?
public static int palautaI() { int i = Lue.rivi(); return i; } public static void main(String[] args) { int luku = palautaI(); }
Kokeile.
Tommittaja kirjoitti:
Edit3: Tuo void- tyyppisessä lauseessa oleva return siis vain lopettaa metodin suorituksen?
Lue tuo edellinen viestini, erityisesti 3. kappale.
selvä, nyt osaan käyttää tuota return- lausetta suurin piirtein. :D
täällä on "lopullinen" tiedosto: http://omatohjelmat.webs.com
ja README tiedosto.
Edit: ja tuo ohjelmieni "huono" ulkoasu tulee, kun esim notepad++ ja BlueJ sisentävät ne automaattisesti.
Jos ohjelma sisentää automaattisesti huonosti, niin muuta ohjelman asetuksia, tai ota automatiikka pois käytöstä ja sisennä manuaalisesti.
Kylläpäs hyvin oudosti sisentävät nuo, kun esimerkiksi if -lohkon päättävä sulje on eri lohkoissa mahdollisesti eri kohtiin tasattu. Samoin while lause näkyisi välillä ilmeisesti sisentyvän eri tavoin. Arvaan, että noissa käytetään jonkinlaista satunnaisuutta editorin puolelta. Tuota kyllä hieman harvemmin näkyy tapahtuvan, tiedä sitten miten noita painotetaan siellä editorin koodissa.
miten tällainen on väärin:
public class Laskin { /*************************************************************************************************************/ public static void plussa() { System.out.println("\nPlus-laskenta 0 lopettaa\n========================="); int i = 0; double summa = 0; while ( i == 0 ) { double luku = Lue.dluku(); summa += luku; if (luku == 0) { System.out.println("Lopetit plussaamisen"); break; } } System.out.println("Lukujen summa on: "+summa); } /*************************************************************************************************************/ public static void miinus() { System.out.println("\nMiinus-laskenta 0 lopettaa\n==========================="); int i = 0; double alkuluku = Lue.dluku(); if (alkuluku == 0) { System.out.println("Lopetit miinustamisen"); return; } double erotus = 0; while ( i == 0 ) { double luku = Lue.dluku(); erotus = alkuluku -= luku; if (luku == 0) { System.out.println("Lopetit miinustamisen"); i = 1; break; } } System.out.println("Lukujen erotus on: "+erotus); } /*************************************************************************************************************/ public static void jako() { System.out.println("\nJako-laskenta 0 lopettaa\n========================="); int i = 0; double osamaara = Lue.dluku(); if (osamaara == 0) { System.out.println("Lopetit jakamisen"); return; } while ( i == 0 ) { double luku = Lue.dluku(); if (luku == 0) { System.out.println("Lopetit Jakamisen"); break; } else { osamaara /= luku; } } System.out.println("Lukujen osamäärä on: "+osamaara); } /*************************************************************************************************************/ public static void kerto() { System.out.println("\nKerto-laskenta 0 lopettaa\n=========================="); int i = 0; double tulo = Lue.dluku(); if (tulo == 0) { System.out.println("Lopetit Kertomisen"); return; } while ( i == 0 ) { double luku = Lue.dluku(); if (luku == 0) { System.out.println("Lopetit Kertomisen"); break; } else { tulo *= luku; } } System.out.println("Lukujen tulo on: "+tulo); } /*************************************************************************************************************/ public static void potenssi() { System.out.println("\nPotenssi 0 lopettaa\n===================="); int i = 0; double tulos = 0; while (i == 0) { System.out.println("Syötä kantaluku"); double f = Lue.dluku(); if (f == 0) { System.out.println("Lopetit potenssilaskennan"); break; } System.out.println("Syötä eksponentti (0.5 tarkoittaa neliöjuurta)"); double p = Lue.dluku(); if (p == 0) { System.out.println("Lopetit potenssilaskennan"); i = 1; break; } else { tulos = Math.pow(f, p); System.out.println("Lukujen tulos on: "+tulos); } } } /*************************************************************************************************************/ public static void main(String[] args) { int komento; do { System.out.println("\n\nLaskin - 0 lopettaa\n===================\n\n1 - Plus-laskenta\n\n2 - Miinus-laskenta\n\n3 - jakolasku\n\n4 - Kertolaskenta\n\n5 - Potenssilaskenta\n"); komento = Lue.kluku(); switch (komento) { case 1: plussa(); break; case 2: miinus(); break; case 3: jako(); break; case 4: kerto(); break; case 5: potenssi(); break; case 0: System.out.println("Lopetit laskimen"); break; default: System.out.println("Virhe syötteessä"); break; } } while (komento != 0); } }
Siis tuohan on täysin oikein.. kaikki sulkeet ovat oikeissa kohtaa ja menee muutenkin oikein.. mitä ihmettä tässä on väärin??
Eihän tuossa syntaktisesti taida mitään väärää olla, ainostaan potenssi, kerto ja main metodeista löytyy hieman muusta koodista poikkeavia sisennyksiä. Tuolla edellisellä viestilläni otin vain hieman kantaa siihen, kun sanoit että "ohjelmiesi", millä ilmeisesti meinasit lähdekoodia, ulkoasu on huono johtuen siitä, että editori sisentää automaattisesti.
Melko hyvinhän tuo on sisennetty, jos vain käyttää samaa sisennys tyyliä kaikkialla koodissa. Nuo editorit tekevät sen yleensä automaattisesti.
Tietty, jos koodia on tarkoitus kommentoida, niin minusta siinä on vielä parantamista, kuten muutama viesti sitten kirjoittelin ja esitin, miten omasta mielestäni sitä voisi parantaa. Sieltä voi käydä lukemassa.
Lisäksi mainitaan nyt vielä tässä (jos ei jaksa aiempaa viestiä lukea), että minusta esimerkiksi vaikka juuri tuo kertolasku ei toimi oikein, sillä jos käyttäjäsyöttää luvut 8 ja 0, niin se ilmeisesti tulostaa 8. Mutta eihän tuossa ole kuin vasta yksi luku, joten tuloa ei minusta oikein voi laskea.
voin kyllä laittaa että jos luku on 0 tulo == 0;
Edit: korjattu on! laitoin vain if lauseeseen että tulo = 0;
Edit2: saman laitoin kotisivullenikin: http://omatohjelmat.webs.com
Edit3: joo huomasin tuossa potenssissa pientä häikkää.. korjaanpa sen heti
PS: en jaksa laitaa sitä nyt sivulleni..
tässä se potenssi on:
public static void potenssi() { System.out.println("\nPotenssi 0 lopettaa\n===================="); int i = 0; double tulos = 0; while (i == 0) { System.out.println("Syötä kantaluku"); double f = Lue.dluku(); if (f == 0) { System.out.println("Lopetit potenssilaskennan"); break; } System.out.println("Syötä eksponentti (0.5 tarkoittaa neliöjuurta)"); double p = Lue.dluku(); if (p == 0) { System.out.println("Lopetit potenssilaskennan"); i = 1; break; } else { tulos = Math.pow(f, p); System.out.println("Lukujen tulos on: "+tulos); } } }
laitoin tuon uuden version sivulleni, mutta se ei muutu se tiedosto! muutin sisennystä ja uploadasin, eikä se siltikään muutu sillä sivulla!!
Mietin, että miksi ihmeessä jaksan vastailla aina uudelleen ja uudelleen tähän aiheeseen, toki mielelläni tänne sivustolle kirjoittelen, mutta...
No kuitenkin, et Tommittaja tainnut testata kunnolla laskintasi tuon muutoksen jälkeen. Ehkä jo huomasit, että se kertolaskukin on rikki. Ilmeisesti et oikeasti kovin paljoa mieti koodiasi läpi, että mitä se tekee missä vaiheessa.
Toki se aiempi versio oli ihan toimiva ja varmasti riittää johonkin käyttöön ja vasta kerta opettelemassa olet. Toivottavasti kuitenkin olet lukenut ajatuksen kanssa, mitä täällä monet ovat neuvoneet sinua. Minusta olet saanut paljon neuvoja eri käyttäjiltä, joita noudattamalla voisit saada helpommin parempaa koodia. En viitsi samoja asioita toistaa kauheasti enää tässä, lue vain aiemmit viestini tähän ketjuun, niin ymmärrät ehkä minun pointtini.
Tuo potenssikoodi näyttää melko hyvältä, vaikkakin muuttuja i on turha. Mieti, miten saat helposti while:n ehdon olevan aina tosi ja näköjään tiedätkin millä sen silmukan voit lopettaa.
oho... toi kertolasku todellakin on rikki, koska jos sen toiminnon lopettaa, niin se on AINA 0.. pitää korjata. kiitos neuvoista
Edit: laitoin tuon while loopin tyhjäksi, jotta se on aina true.. teinkö oikein?
PS: sain sen tuonne sivustollekin oikein nyt.. :D
opettelin juuri käyttämään tuota scanner- luokkaa.. Helppokäyttöinen on. :D
onko tuossa scannerissa mitään nextLong/nextFloat- metodia?
Siihen on ainakin kolme parempaakin tapaa selvittää kuin kysyminen, ja kaiken lisäksi kaikki niistä antaisivat vastauksen jokseenkin välittömästi:
1) kokeile
2) lue dokumentaatiosta http://java.sun.com/javase/6/docs/api/
3) käytä editoria joka osaa kertoa mitä metodeita luokan rajapinnasta löytyy, esim. eclipseä.
Ja vastaus on siis että kyllä on.
Teinpä tässä huvikseni oman toteutuksen laskimesta. Ehkä opit jotain, jos luet ja ymmärrät tämän. Voit kokeilla laajentaa tuota lisäämällä vaikka potenssin tai jonkin muun operaation (uuden operaation pitäisi vaatia vain yhden uuden luokan ja CalculatorCli:hin yhden koodirivin lisäyksen).
Komentorivirajapinta:
package calculator; import java.util.Scanner; public class CalculatorMain { public static void main(String[] args) { Scanner in = new Scanner(System.in); new CalculatorCli(in, System.out).run(); } }
package calculator; import calculator.op.*; import java.io.PrintStream; import java.util.*; public class CalculatorCli implements Runnable { private static final boolean CONTINUE = true; private static final boolean QUIT = false; private final Calculator calc = new Calculator(); private final Map<String, Operation> ops; private final Scanner in; private final PrintStream out; public CalculatorCli(Scanner in, PrintStream out) { this.ops = initOperations(); this.in = in; this.out = out; } private static Map<String, Operation> initOperations() { Map<String, Operation> ops = new HashMap<String, Operation>(); ops.put("+", new Plus()); ops.put("-", new Minus()); ops.put("*", new Multiply()); ops.put("/", new Divide()); return Collections.unmodifiableMap(ops); } public void run() { do { printResult(); } while (processUserInput()); } private void printResult() { out.println(calc.getResult()); out.print("> "); } private boolean processUserInput() { if (in.hasNextDouble()) { return numberWasEntered(); } else { return operatorWasEntered(); } } private boolean numberWasEntered() { calc.inputValue(in.nextDouble()); return CONTINUE; } private boolean operatorWasEntered() { Operation op = ops.get(in.next()); if (op != null) { calc.inputOperation(op); return CONTINUE; } else { return QUIT; } } }
Itse laskin ja sen laskuoperaatiot:
package calculator; import calculator.op.*; public class Calculator { private static final NullOperation NULL_OPERATION = new NullOperation(); private double result; private Operation operation = NULL_OPERATION; public double getResult() { return result; } public void inputValue(double value) { result = operation.apply(result, value); inputOperation(NULL_OPERATION); } public void inputOperation(Operation operation) { assert operation != null; this.operation = operation; } }
package calculator.op; public interface Operation { double apply(double a, double b); }
package calculator.op; public class NullOperation implements Operation { public double apply(double a, double b) { return b; } }
package calculator.op; public class Plus implements Operation { public double apply(double a, double b) { return a + b; } }
package calculator.op; public class Minus implements Operation { public double apply(double a, double b) { return a - b; } }
package calculator.op; public class Multiply implements Operation { public double apply(double a, double b) { return a * b; } }
package calculator.op; public class Divide implements Operation { public double apply(double a, double b) { return a / b; } }
Lopuksi vielä laskimen testitapaukset:
package calculator; import calculator.op.*; import junit.framework.TestCase; public class CalculatorTest extends TestCase { private static final double DELTA = 0.001; private Calculator calc; protected void setUp() throws Exception { calc = new Calculator(); } public void testAtStartupTheResultIsZero() { assertEquals(0.0, calc.getResult(), DELTA); } public void testAfterInputtingANumberThatNumberIsTheResult() { calc.inputValue(42); assertEquals(42.0, calc.getResult(), DELTA); } public void testTheCalculatorCanSum() { calc.inputValue(1); calc.inputOperation(new Plus()); calc.inputValue(2); assertEquals(3.0, calc.getResult(), DELTA); } public void testTheCalculatorCanSubtract() { calc.inputValue(3); calc.inputOperation(new Minus()); calc.inputValue(1); assertEquals(2.0, calc.getResult(), DELTA); } public void testTheCalculatorCanMultiply() { calc.inputValue(2); calc.inputOperation(new Multiply()); calc.inputValue(3); assertEquals(6.0, calc.getResult(), DELTA); } public void testTheCalculatorCanDivide() { calc.inputValue(2); calc.inputOperation(new Divide()); calc.inputValue(4); assertEquals(0.5, calc.getResult(), DELTA); } public void testWhenANumberIsInputtedBeforeChoosingTheOperatorThenTheNewNumberIsTheResult() { calc.inputValue(1); calc.inputOperation(new Plus()); calc.inputValue(2); calc.inputValue(99); assertEquals(99, calc.getResult(), DELTA); } }
Jackal von ÖRF kirjoitti:
testWhenANumberIsInputtedBeforeChoosingTheOperatorThenTheNewNumberIsTheResult
vau :)
Mie ja!
import java.io.*; class Calculator { public static void main(String argv[]) { try { Process ls_proc = Runtime.getRuntime().exec("calc"); } catch (IOException ioe) { System.err.println(ioe); System.exit(1); } System.exit(0); } }
os kirjoitti:
Jackal von ÖRF kirjoitti:
testWhenANumberIsInputtedBeforeChoosingTheOperatorThenTheNewNumberIsTheResult
vau :)
Toki metodien nimeämisessä on hyvä käyttää selkeitä ja ennen kaikkea kuvaavia nimiä. Suuremmissa projekteissahan tuo korostuu, mutta hyvähän se on ottaa käytännöksi pienempiäkin värkätessä.
En keksinyt lyhyempää nimeä sille toiminnolle, minkä tuo testi määrittää. :) Keksitkö sinä?
Testikoodissa luettavuus on kaikkein tärkein asia, jotta testiä voisi tarvittaessa päivittää sitten, kun järjestelmän toiminnallisuuteen tulee muutoksia. Nimittäin jos testistä ei käy ilmi, että miksi ja mitä toimintoa varten kyseinen testi on kirjoitettu, niin ennen pitkää testit lahoavat käsiin ja niistä saatavat hyödyt vähenevät. Sitä paitsi testeissä pitkistä nimistä ei ole haittaa, koska kukaan ei joudu kutsumaan niitä käsipelillä. Tuotantokoodissa asia on tietenkin eri, ja silloin noudatetaan näitä ohjeita: http://tottinge.blogsome.com/meaningfulnames/
Joillain ohjelmilla testien nimistä saa myös pulautettua helposti luettavan yhteenvedon (esim. http://www.jdave.org/documentation.html), jolloin tuosta tulisi lause "when a number is inputted before choosing the operator then the new number is the result".
Itse olisin lähestynyt ongelmaa kannalta: lyhyempi nimi, (pidempi) javadoc-kommentti. Kertokaa ihmeessä, jos joku muu tapa on selvästi parempi.
Sopivin ratkaisu riippuu varmaan myös aika paljon siitä, minkä kaikkien vekottimien (esim. tuon JDaven kaltaisten) avustamana projektia käytännössä kyhätään.
Käytän kommentteja koodissa silloin, kun en osaa nimetä metodeja ja muuttujia niin hyvin, että kaikki oleellinen tieto kävisi niistä ilmi. Ja sitten kun kommenttia oikeasti tarvitaan, niin on syytä kirjoittaa niin hyvä kommentti kuin ikinä pystyy.
Joitakin esimerkkejä testeistä, joissa olen käyttänyt kommenttia: syy miksi testi tarvitaan (rivit 164-165), testistä ei käy helposti ilmi että miten se toimii (rivi 174), testi on liian monimutkainen ja vaatii lisäselitystä (rivit 131-132). Tässä vielä pari esimerkkiä tuotantokoodista, jossa olen joutunut käyttämään kommentteja selventämään syytä, että miksi jokin koodi toimii siten kuin se toimii: rivit 59-60, rivit 113-117, rivit 43-47. (Muuten tuossa projektissa ei kovinkaan monta kommenttia ole. Testiluokista löytyy pitkiä metodien nimiä, jos haluatte naureskella niille. ;)
käykää mun sivulla: siellä se on korjattuna
PS: En vielä lukenut viestejänne kokonaan, potenssi oli jo siellä..
http://omatohjelmat.webs.com
Hmmmm... en ole tosiaankaan noin pitkällä, Jackal von ÖRF..
En osaa edes noita packageja
Siinä onkin sitten hyvä syy jatkaa oppikirjan lukemista. Tuon koodin ymmärtämiseksi pitää ymmärtää ainakin rajapinnat (interface) ja kokoelmat (collections). Pakkaukset (package) ovat yksinkertaisin tuossa esiintyvä käsite.
hyvä kun en edes tiedä, mitä "rajapinta" tarkoittaa.. :D
Saati sitten "kokoelma"
Tommittaja kirjoitti:
hyvä kun en edes tiedä, mitä "rajapinta" tarkoittaa.. :D
Saati sitten "kokoelma"
Eikö tässä juuri sitä kehotettu opettelemaan?
Vinkki: Kokeile kirjoittaa seuraavat hakuehdot Googleen.
java interface
java collections
saanko kysyä, kuinka pitkällä nuo rajapinnat ja kokoelmat opitaan?
Sinun kannattaa ehkä ensin panostaa vielä hieman funktioihin, ehtolauseisiin, silmukoihin ja tavallisiin muuttujiin. Ilman niitä voivat nuo muut mainitut mennä hieman yli, ja niiden jälkeen taas voit opetella lisää ihan haluamassasi järjestyksessä.
mikäs minun funktioissa, ehtolauseissa yms. on vikana? katso http://omatohjelmat.webs.com/
siellä se laskin on valmiina...
Tommittaja kirjoitti:
mikäs minun funktioissa, ehtolauseissa yms. on vikana?
Esimerkiksi tämä keskustelu, toisin sanoen se, että joudut kyselemään niistä jatkuvasti. Jos osaisit ne kunnolla, ei olisi tuosta laskimestakaan tarvinnut aloittaa yhtäkään aihetta. (Nythän niitä on jo monta.)
Lisätään nyt listaan vielä yleinen tietämys ohjelman toiminnasta. Minusta tämä sisältyy jo mainittujen asioiden ymmärtämiseen, joten en erikseen maininnut.
Tommittaja kirjoitti:
katso ...
Lopetapa nyt tuo mainostus, olet tunkenut linkin tänne jo aivan riittävän monta kertaa. Luuletko sitä paitsi, että ketään oikeasti kiinnostaa tekemäsi laskin? Maailmassa on kyllä aivan kylliksi laskimia, jotka ovat luotettavampia, käsittelevät laajempaa lukualuetta ja kykenevät laskemaan ihan oikeitakin laskulausekkeita.
joo sori toi mainostus en tahallani yrittänyt, itseänikin hieman häiritsi se, mutta ei ollut koodia nyt tässä ja en jaksanut sitä ottaa mistään..
Tommittaja kirjoitti:
joo sori toi mainostus en tahallani yrittänyt, itseänikin hieman häiritsi se, mutta ei ollut koodia nyt tässä ja en jaksanut sitä ottaa mistään..
Sivuistas sen verran, että kannattais laittaa .java-tiedostojen lisäksi valmiiksi käännetyt .class tiedostot sinne.
joo vois laittaa kiitos neuvosta laitan ne sinne kohta... ei tarvitse lataa jdk:ta..
Nyt on valmis siellä on kaikki classit ja ohjeet ajamiseen
kertomabugi on nyt korjattu! nyt 45*0 = 0 eikä 45.. :D
Olimpa taas ahkera ja katson koodiasi, en tiedä miksi. Saattoi johtua uteliaisuudesta, että millasta koodia nyt siellä oikein on. Jos kyseessä on kuitekin tuo koodi, minkä olet sivuillesikin päivittänyt, niin se ei toimi oikein. Esim. jos käyttäjä syöttää luvut 4,5, 20 ja päättävän nollan, niin saa tulokseksi 80.
hmm.. mikäköhän se ongelma on? pitää katsoa.. kiitos vinkistä
nyt toimii, olin unohtanut kertomisen kierros1:sestä
Ootas laitan sivulle
Nyt ainakin se .java toimii testasin aika kunnolla
Moi!
Voisi olla opettavaista, jos kokeilisit toteuttaa laskimen, joka osaisi käsitellä yksinkertaisia lausekkeita. RPN-lausekkeesta (Reverse Polish Notation) olisi helppo lähteä liikkeelle, jos pinon käsite on tuttu.
En osaa Javaa, mutta alla on esimerkkinä Infernon Limbolle toteutettu yksinkertainen RPN-laskin:
# Reverse Polish Notation laskin RPNlaskin(lauseke : string) : (string, real) { (words, tokens) := sys->tokenize(lauseke, " "); if(words == 0) return ("no tokens for RPNcalculator", 0.0); token : string; calcstack : list of real; stacktop : real; tulos : real; while(tokens != nil) { token = hd tokens; case token { "+" => if(len calcstack > 1) { stacktop = hd calcstack; calcstack = tl calcstack; tulos = hd calcstack; tulos += stacktop; calcstack = tl calcstack; calcstack = tulos :: calcstack; } else return ("Calculator stack empty", 0.0); "-" => if(len calcstack > 1) { stacktop = hd calcstack; calcstack = tl calcstack; tulos = hd calcstack; tulos -= stacktop; calcstack = tl calcstack; calcstack = tulos :: calcstack; } else return ("Calculator stack empty", 0.0); "*" => if(len calcstack > 1) { stacktop = hd calcstack; calcstack = tl calcstack; tulos = hd calcstack; tulos *= stacktop; calcstack = tl calcstack; calcstack = tulos :: calcstack; } else return ("Calculator stack empty", 0.0); "/" => if(len calcstack > 1) { stacktop = hd calcstack; calcstack = tl calcstack; tulos = hd calcstack; if(stacktop != 0.0) { tulos /= stacktop; } else return ("Divide by zero", 0.0); calcstack = tl calcstack; calcstack = tulos :: calcstack; } else return ("Calculator stack empty", 0.0); * => if(isreal(token)) { calcstack = real token :: calcstack; } else return ("Calculator syntax error", 0.0); } tokens = tl tokens; } if(len calcstack != 1) return ("Calculator stack not empty", 0.0); else return ("", hd calcstack); } isreal(input : string) : int { parse := 1; count := len input; dots := 0; if(count == 0) return 0; i := 0; if(input[i] == '-') i++; for(i = i; i < count; i++) { if(input[i] < '0' || input[i] > '9') { parse = 0; if(input[i] == '.') { dots ++; parse = 1; } if(dots > 1 || parse == 0) return 0; } } return parse; }
jaa.. en kyllä osaa tuommoisia kun en edes tiedä, miten tuo menisi javalla..
lainaus:
hmm.. mikäköhän se ongelma on?
Ongelma on sama johon vastasin aikaisemmassa ketjussa. Itseasiassa sama virhe on näissä kaikissa. Miten onnistuit saamaan tismalleen saman virheen uudestaan jos kerta sait viimeksi sen toimimaan?-)
Miltei sama vika, siis sama vika periaatteessa, mutta olin vain unohtanut laittaa kertomisen siihen bugin korjaamiseen, siihen missä se sanoi aina että luku * 0 = luku..
Hmm.. en ole ikinä kuullutkaan mistään "pinon käsitteestä", enkä luultavasti siis osaisi tehdä sellaista laskintakaan... :D
Tommittaja kirjoitti:
Hmm.. en ole ikinä kuullutkaan mistään "pinon käsitteestä", enkä luultavasti edes osaisi tehdä sellaista laskinta... :D
Aika vaikea uskoa että et ole koskaan kuullut pinosta.
Löytyykö sinulta vaikka 5 kolikkoa. Jos laitat ne päällekkäin niin ne on pinossa.
Kuten huomaat, niin voit helposti ottaa pinon päällimmäisen kolikon ja sen jälkeen toiseksi päällimmäisen.
Eli jos sinulla on taskun pohjalla kolikoita, niin voit luontevasti lisäillä niitä aina pinon päällimmäiseksi. Kun sitten otat niitä pois pinosta, niin saat aina ensimmäiseksi sen, jonka olet viimeiseksi lisännyt, jne.
jaa... en kyllä tiedä miten tuo esimerkkikoodi menisi javalla?
Pino on hyvin yksinkertainen tietorakenne toteuttaa, joten voitkin hyvänä harjoituksena kirjoittaa oman pinoluokan. Jottet heti masennu osaamattomuuteen, tässä on runko kommentteineen. Täytä vain puuttuvat koodirivit. Luokassa on valmiiksi myös testikoodi, jonka pitäisi luokan valmistuttua tulostaa seuraavat rivit:
1: 17 2: 13 3: 11
public class IntegerPino { private Integer[] luvut; private int maara; public IntegerPino() { // Varataan aluksi tilaa vaikka yhdelle. Määrä on tietenkin 0. luvut = new Integer[1]; maara = 0; } public void lisaa(Integer arvo) { // Täydennä: // Jos taulukko on jo täynnä... { // Luodaan isompi taulukko. // Kopioidaan arvot silmukassa uuteen taulukkoon. // Korvataan vanha taulukko uudella. // } // Laitetaan vapaaseen kohtaan uusi arvo ja nostetaan laskuria. } public Integer poista() throws Exception { // Tarkistetaan koko if (koko() == 0) { throw new Exception("Pino on tyhjä!"); } // Täydennä: // Palautetaan pinosta viimeinen arvo, pienennetään laskuria. } public int koko() { return maara; } public static void main(String[] args) { // Luodaan luokka ja lisätään luvut pinoon. IntegerPino p = new IntegerPino(); p.lisaa(11); p.lisaa(13); p.lisaa(17); // Otetaan pinosta lukuja niin kauan, kuin niitä on. try { for (int i = 1; p.koko() != 0; ++i) { System.out.println(i + ": " + p.poista()); } } catch (Exception e) { // Tänne ei tietenkään pitäisi joutua. System.out.println("Virhe: " + e.getMessage()); } } }
en tosiaankaan ole käyttänyt taulukoita paljoa, joten en tiedä, miten arvoja kopioidaan taulukkoon?
Arvon kopiointi taulukkoonhan on melkein kaikissa ohjelmointi kielissä jokseenkin: taulukko[alkio] = arvo;
Omassa RPN-laskimessani en oikeastaan käyttänyt pinoa, vaan listan avulla toteutettua jonoa. Pinon simppeli toteutus taulukkoa käyttämällä, Infernon Limbolla voisi olla vaikkapa allaolevan tapainen:
pino : array of string; #taulukko merkkijonoille, toimii pinona push(token : string) { i := len pino uusipino := array[len pino + 1] of string; (uusipino[0:], uusipino[i], uusipino[i+1:]) = (pino[0:i], token, pino[i:]); pino = uusipino; } pop() : (string, string) { if(len pino > 0) { token := pino[len pino - 1]; pino := pino[0: len pino - 1]; return (token, ""); } else return ("", "Pino on tyhjä"); }
saanko kysyä, mikä on Infernon limbo?
Tommittaja kirjoitti:
saanko kysyä, mikä on Infernon limbo?
Google + Inferno + Limbo > http://www.vitanuova.com/inferno/limbo.html
Olet aivan uskomattoman kyvytön tiedonhaussa. Jos et yhtään opi sitä, voin melkeinpä taata, ettet ikinä pääse ohjelmoinnissa kovin pitkälle.
Inferno on käyttöjärjestelmä, jota voi ajaa natiivina tai sitten yleisimpien käyttöjärjestelmien päällä. Limbo on ohjelmointikieli Infernon sovelluskehitykseen.
joo, ei ajatus kulje enää kun on väsynyt, joten laitoin vain kysymyksen, sori siitä ei tuo ko. ajatus kulje.. :|
Joko olet päässyt jyvälle RPN-lausekkeen ratkaisemisesta pinon avulla?
Tein alle toimivan esimerkki toteutuksen pinoa käyttävästä RPN-laskimesta Limbolla, joka samalla on myös yksinkertainen client-server esimerkki.
Serveri: tarvitsee parametrikseen portin ja jää odottelemaan yhteydenottoja. Jokainen saapuva yhteys hoidetaan omassa säikeessään, jossa saapuva RPN-lauseke ratkaistaan ja tulos lähetetään takaisin clientille. Ohjelma kirjoittaa debuggaus tietoa ikkunaan.
Client: toimii komentoriviltä, parametreinä osoite ja viesti (RPN-lauseke).
esim. client ip-osoite!portti '5 5 + 2 -'
server.b
implement Server; include "sys.m"; sys: Sys; Connection : import Sys; include "draw.m"; draw: Draw; Screen, Display, Image, Context, Point, Rect: import draw; include "tk.m"; tk: Tk; Toplevel: import tk; include "tkclient.m"; tkclient: Tkclient; Resize, Hide, Help : import tkclient; Server : module { init : fn(ctxt : ref Draw->Context, argv : list of string); }; msg_cfg := array[] of { "frame .msg", "scrollbar .msg.scroll -command {.msg.t yview}", "text .msg.t -width 15c -height 7c -state disabled -yscrollcommand {.msg.scroll set} -bg white", "pack .msg.t -side left -expand 1 -fill both", "pack .msg.scroll -side left -fill y", "pack .msg -expand 1 -fill both -padx 5 -pady 5", "pack propagate . 0", "update" }; Pino : adt { pino : array of real; koko : int; uusi : fn() : ref Pino; push : fn(pino : self ref Pino, value : real); pop : fn(pino :self ref Pino) : (real, string); }; ctxt: ref Draw->Context; main : ref Tk->Toplevel; init(xctxt : ref Draw->Context, argv : list of string) { sys = load Sys Sys->PATH; if(int (len argv) != 2) { sys->fprint(sys->fildes(2), "usage: %s port\n", hd argv); raise "fail:bad usage"; } sys->pctl(Sys->NEWPGRP, nil); draw = load Draw Draw->PATH; tk = load Tk Tk->PATH; tkclient = load Tkclient Tkclient->PATH; tkclient->init(); if (xctxt == nil) xctxt = tkclient->makedrawcontext(); ctxt = xctxt; param := tl argv; port := hd param; # First, announce the service. This creates a line directory # and conn.cfd will be open on the ctl file (n, conn) := sys->announce("tcp!*!" + port); if (n < 0) { sys->fprint(sys->fildes(2), "Server: announce failed\n"); raise "fail:announce failed"; } spawn listenthread(conn); (t, wmctl) := tkclient->toplevel(ctxt, nil, "Message Window", Tkclient->Appl); if(t == nil) return; main = t; cmd := chan of string; tk->namechan(main, cmd, "cmd"); for (c:=0; c<len msg_cfg; c++) tk->cmd(main, msg_cfg[c]); tkclient->startinput(main, "ptr" :: "kbd" :: nil); tkclient->onscreen(main, nil); for(;;) alt { s := <-main.ctxt.kbd => tk->keyboard(main, s); s := <-t.ctxt.ptr => tk->pointer(main, *s); s := <-main.ctxt.ctl or s = <-main.wreq or s = <-wmctl => tkclient->wmctl(main, s); } } Pino.uusi() : ref Pino { p := ref Pino (nil, 0); return p; } Pino.push(p : self ref Pino, value : real) { i := p.koko; uusipino := array[i + 1] of real; (uusipino[0:], uusipino[i], uusipino[i+1:]) = (p.pino[0:i], value, p.pino[i:]); p.pino = uusipino; p.koko++; } Pino.pop(p : self ref Pino) : (real, string) { if(len p.pino > 0) { value := p.pino[p.koko - 1]; p.pino = p.pino[0: p.koko - 1]; p.koko--; return (value, ""); } else return (0.0, "Pino on tyhjä"); } listenthread(conn : Connection) { # Now, listen for incoming connections, spawn new thread # for each incoming connection. while (1) { listen(conn); } } listen(conn : Connection) { (ok, c) := sys->listen(conn); if (ok < 0) { sys->fprint(sys->fildes(2), "Server: listen failed\n"); raise "fail:listen failed"; } spawn workerthread(c); } getip(address: string): string { (nil, tokens) := sys->tokenize(address, "!"); ip:= hd tokens; return ip; } workerthread(conn : Connection) { buf := array [sys->ATOMICIO] of byte; # The connections data file is not opened by default, # must explicitly do so to accept the new connection rdfd := sys->open(conn.dir + "/data", Sys->OREAD); wdfd := sys->open(conn.dir + "/data", Sys->OWRITE); rfd := sys->open(conn.dir + "/remote", Sys->OREAD); lfd := sys->open(conn.dir + "/local", Sys->OREAD); n := sys->read(rfd, buf, len buf); remote:= getip(string buf[:n-1]); n = sys->read(lfd, buf, len buf); local := string buf[:n-1]; msg : string; n = sys->read(rdfd, buf, len buf); (tulos, virhe) := RPNlaskin(string buf[0:n]); if(virhe == "") { msg = "[" +remote +"] : " + string buf[0:n] + " = " +string tulos +"\n"; sys->write(wdfd, array of byte ("Response from " + local +" :tulos = " +string tulos +"\n" ), len ("Response from " +local +" :tulos = " +string tulos +"\n" )); } else { msg = "[" +remote +"] : " + string buf[0:n] + " = error: " +virhe +"\n"; sys->write(wdfd, array of byte ("Response from " + local +" :error: " +virhe +"\n" ), len ("Response from " + local +" :error: " +virhe +"\n" )); } tk->cmd(main, ".msg.t insert end '"+msg); tk->cmd(main, "update"); } # Reverse Polish Notation laskin RPNlaskin(lauseke : string) : (real, string) { pino := Pino.uusi(); (words, tokens) := sys->tokenize(lauseke, " "); if(words == 0) return (0.0, "no tokens for RPNcalculator"); token : string; token1 : real; token2 : real; err : string; while(tokens != nil) { token = hd tokens; case token { "+" => if(pino.koko > 1) { (token1, err) = pino.pop(); (token2, err) = pino.pop(); token2 += token1; pino.push(token2); } else return (0.0, "Calculator stack empty"); "-" => if(pino.koko > 1) { (token1, err) = pino.pop(); (token2, err) = pino.pop(); token2 -= token1; pino.push(token2); } else return (0.0, "Calculator stack empty"); "*" => if(pino.koko > 1) { (token1, err) = pino.pop(); (token2, err) = pino.pop(); token2 *= token1; pino.push(token2); } else return (0.0, "Calculator stack empty"); "/" => if(pino.koko > 1) { (token1, err) = pino.pop(); (token2, err) = pino.pop(); if(token1 != 0.0) { token2 /= token1; } else return (0.0, "Divide by zero"); pino.push(token2); } else return (0.0, "Calculator stack empty"); * => if(isreal(token)) { pino.push(real token); } else return (0.0, "Calculator syntax error"); } tokens = tl tokens; } if(pino.koko != 1) return (0.0, "Calculator stack not empty"); else { (token1, err) = pino.pop(); return (token1, ""); } } isreal(input : string) : int { parse := 1; count := len input; dots := 0; if(count == 0) return 0; i := 0; if(input[i] == '-') i++; for(i = i; i < count; i++) { if(input[i] < '0' || input[i] > '9') { parse = 0; if(input[i] == '.') { dots ++; parse = 1; } if(dots > 1 || parse == 0) return 0; } } return parse; }
client.b
implement Client; include "sys.m"; sys: Sys; Connection : import Sys; include "draw.m"; Client : module { init : fn(nil : ref Draw->Context, argv : list of string); }; init(nil : ref Draw->Context, argv : list of string) { sys = load Sys Sys->PATH; if(int (len argv) != 3) { sys->fprint(sys->fildes(2), "usage: %s address 'message'\n", hd argv); raise "fail:bad usage"; } param := tl argv; address := "tcp!" + hd param; param = tl param; message := hd param; (n, conn) := sys->dial(address, ""); if (n < 0) { sys->fprint(sys->fildes(2), "Client: connection refused\n"); raise "fail:connection refused"; } req := array of byte message; sys->write(conn.dfd, req, len req); buf := array [sys->ATOMICIO] of byte; n = sys->read(conn.dfd, buf, len buf); sys->print("\n%s\n", string buf[:n]); }
Aihe on jo aika vanha, joten et voi enää vastata siihen.