Moro!
Oon tässä koodailemassa C:llä tommosta pikku ohjelmaa, jonka tarkoitus on lukea kokonaisluku ja tulostaa se sanallisessa muodossa. Olisiko jotain hyviä ehdotuksia homman toteuttamiseksi? Lähinnä pohdituttaa se, että kuinka saan esim. suuren kokonaisluvun ollessa kyseessä tietoon sen, monennessako numerossa ollaan menossa.
Esim.
Luku: 100027
Tarkoituksena ei siis luonnollisestikaan ole tulostaa "Yksi Nolla Nolla....". Eli onko C:ssä jokin funktio, jolla voisi käydä kokonaisluvun numero kerrallaan läpi, jotta saisi oikein tulostettua esim. ko. luvun "Satatuhattakaksikymmentäseitsemän".
Vai olisiko ko. ongelman ratkaisemiseen jotain parempaa lähestymistapaa. Hyviä ideoita otetaan vastaan.
Olen vasta aloitellut kunnolla käpistelemään C:llä, joten hakusessa on..
Se, monennessako numerossa ollaan menossa, on helppo selvittää. Luet lukua vasemmalta oikealle ja kun siirryt lukemaan seuraavaa numeroa, kasvatat laskuria yhdellä.
Ongelma ratkeaa varmaankin helpoiten siten, että mietit ensiksi mitkä osat numerossa minkäkin tavun/sanan osan. Esimerkiksi luvussa 142 ainoastaan numero 4 tuottaa tekstiin osan "kymmentä" ja luvussa 213 1 osan "toista". Sitten mietit missä järjestyksessä tekstin osat pitää laittaa paikoilleen ja mihin kohtaan välilyönti/välilyönnit tulevat. Kun mietit huolellisesti kaikki tapaukset läpi, pitäisi koodaamisen olla varsin suoraviivaista ja helppoa.
Lukua kannattaa tarkastella kolmen numeron osissa. Nämä osat muodostetaan aina samalla tavalla, väliin tulee vain sopiva pääte (tuhat(ta), miljoona(a), miljardi(a) jne.).
Esim. 123456789
sata|kaksikymmentä|kolme miljoonaa
neljäsataa|viisikymmentä|kuusi tuhatta
seitsemänsataa|kahdeksankymmentä|yhdeksän
Jep, kiitti vaan vinkeistä. En kyllä vaan vieläkään ole päässyt kärryille, että miten luvun saa tutkittua numero kerrallaan. Onko Scanfille joku spesiaalimerkintä, jolla saa luettua numero kerrallaan, ja siten voisi pitää kirjaa siitä, monennessako luvun numerossa ollaan menossa?
C:llä koodaaminen on Javan jälkeen kuin opettelisi kävelemään juoksemisen opettelun jälkeen. :)
Konvertoi luku sprintfillä merkkitaulukoks ja käy se yks kerrallaan läpi.
Täällä on koodaamani Python-skripti, joka tulostaa luvun tekstinä: http://mureakuha.com/koodikirjasto/686 . Ehkä tuosta on jotain apua, vaikka kieli onkin eri.
Tuo oma koodini käy siis luvun läpi kolmen numeron pätkissä oikealta vasemmalle. Tekstimuodossa lukuihin tulee välilyönti juuri noihin kohtiin ( http://www.cs.tut.fi/~jkorpela/kielenopas/4.11.
Oon koittanu tolla Chimanin logiikalla lähtä liikkeelle, mutta probleemaa pukkaa. Tuo menee kääntäjästä läpi, mutta yllättäen jotain häikkää tossa on.. Joku huomaa varmaan heti tosta mogat.. Kaikki parannusehdotukset erittäin tervetulleita.
#include <stdio.h> #include <string.h> char * kymmenettekstiksi(unsigned long * luku); char ykkoset[11][10] = {"", "yksi", "kaksi", "kolme", "neljä", "viisi", "kuusi", "seitsemän", "kahdeksan", "yhdeksän", "kymmenen"}; char tuhannet[8][10] = {"yksi", "tuhat", "tuhatta", "miljoona", "miljoonaa", "miljardi", "miljardia"}; int main(void) { char * mjono [15]; unsigned long luku; printf("Anna luku "); scanf("%u", &luku); *mjono=kymmenettekstiksi(&luku); printf("Merkkijono on : %c", mjono); return (0); } char * kymmenettekstiksi(unsigned long * luku){ if (*luku < 11) return ykkoset[*luku]; if (*luku < 20) return strcat(ykkoset[(*luku%10)], "toista"); }
1. Ota mjonosta kokomääritys ([15]) pois.
2. *mjono -> mjono
3. Tulostukseen merkkijono %s eikä merkki %c
*Ja vielä käytettävyysvinkkinä: Tulosta rivin loppuun \n, eli rivinvaihto.
Käyttämäsi strcat ei toimi alkuunkaan noin. Tarvitset riittävän ison char-taulukon valmiiksi, koska strcat ei sellaista varaa. Tämän char-taulun voi luoda pääohjelmassa ja välittää char*-tyyppisenä funktiolle. strcat-funktio liittää ensimmäisen parametrin loppuun toisen parametrin. Palautusarvolla ei ole käytännön merkitystä.
Lukua ei ole hyvä välittää osoittimena, koska siitä ei ole mitään hyötyä.
Olisi kaikesta huolimatta voinut sanoa, mitä ohjelma tekee väärin, vaikka sen tuosta helposti näkeekin.
Noilla minun ohjeilla saa ohjelman toimimaan ainakin käyttäjän kannalta oikein, mutta oikeaoppisuudesta en tiedä.
Metabolix kirjoitti:
Käyttämäsi strcat ei toimi alkuunkaan noin. Tarvitset riittävän ison char-taulukon valmiiksi, koska strcat ei sellaista varaa. Tämän char-taulun voi luoda pääohjelmassa ja välittää char*-tyyppisenä funktiolle. strcat-funktio liittää ensimmäisen parametrin loppuun toisen parametrin. Palautusarvolla ei ole käytännön merkitystä.
Kyllä noilla ezulin vinkeillä pelittää ainakin. Eli siis strcat teki juuri sen, jonka oletinkin sen tekevän.
Toimiihan se - kerran. Yritä laittaa loppuun hyppy alkuun, niin lukutekstitaulun sisältö voi olla hieman erilainen kuin ensimmäisellä kerralla.
No enpä ollut asiaan niin tarkasti perehtynyt.
Käyttämällä apumuuttujaa homma pelittää.
char * kymmenettekstiksi(unsigned long *luku){ char * palautus; if(*luku < 11){ strcpy(palautus,ykkoset[*luku]); } else if(*luku < 20){ strcpy(palautus,ykkoset[(*luku%10)]); strcat(palautus, "toista"); } return palautus; }
Näköjään strcpy toimii sijoittamisessa paremmin kuin pelkkä =
No ei kyllä varmasti toimi noin. Segmentation fault. Saa sitä dokumentaatiotakin lukea. Siellä sanotaan aivan selvästi (kuten minäkin sanoin), että tuo funktio ei varaa muistia. ensimmäisen parametrin osoittaman muistilohkon pitää olla riittävän iso lopputulokselle.
Jos ei muistinhallinta ole oikein hanskassa, on melkeinpä turvallisinta a) opetella se tai b) ottaa käyttöön C++ ja sen mukavat perusluokat, joilla tuollainen onnistuu ilman sen kummempia pohjatietoja.
Kuten sainoin, "minun ohjeilla saa ohjelman toimimaan ainakin käyttäjän kannalta oikein, mutta oikeaoppisuudesta en tiedä."
Jos se sanoo Segmentation fault ja sammuu, se ei minun nähdäkseni ole käyttäjän kannalta oikein. Jos Windows antaa kuitenkin kirjoittaa keskelle muistia melkoisen sattumanvaraiseen kohtaan, niin se on Windowsin bugisuutta.
Ei anna Windows kirjoittaa ihan mihin vaan kohtaan muistiin...
Kyllä se aika äkkiä viheltää pelin poikki.
Tommosella ton nyt saa ainakin toimii:
Aika kämäsen näköstä koodiahan tuo kylläkin on...
char * kymmenettekstiksi(unsigned long luku, char * taulukko){ if (luku < 11) return ykkoset[luku]; if (luku < 20){ strcat(taulukko, ykkoset[luku%10]); return strcat(taulukko, "toista"); } else { strcat(taulukko, ykkoset[luku/10]); strcat(taulukko, "kymmenta"); strcat(taulukko, ykkoset[luku%10]); } return taulukko; }
Nyt pitäs noille isommille luvuille keksiä vaan samanlainen käpöstely...
Turha sitä taulukkoa on palautuksessa antaa jos siihin on jo osoitin, vai mitä expertit sanoo?
Snifferi kirjoitti:
Ei anna Windows kirjoittaa ihan mihin vaan kohtaan muistiin...
Kyllä se aika äkkiä viheltää pelin poikki.
Riippuu windowsin versiosta. Itse olen monta kertaa huomannut, että win98 antaa aina välillä(sattumanvaraisesti) kirjotella vähän sinne tänne muistia, mutta XP:llä ajettaessa meno katkeaa heti. Vaikuttaa siltä, että muistin käsittelyä on muutenkin rukattu XP:een
Tuo muistin käsittely sitten onkin ainoa asia mikä 98:ssa päähän ottaa. Pitäisi varmaan asentaa toinen käyttis rinnalle testailuja varten(Eikä sitten mitään ehdotuksia, tiedän kyllä mitä asennan, jos asennan!).
ezuli kirjoitti:
Turha sitä taulukkoa on palautuksessa antaa jos siihin on jo osoitin, vai mitä expertit sanoo?
Joo, on turha. Mulla oli tossa ns. tuplapalautus eli pointterin ja returnin kautta.
Kyllä se on varsin kätevä laittaa juuri noin, koska tuolla tavalla funktiota voi käyttää suoraan kirjoittamiseen:
printf("Luku %i on '%s'\n", luku, kymmenettekstiksi(luku, aputaulukko));
Aputaulukon pitää sitten olla riittävän iso, jottei mene vahingossa yli.
Suosittelen sellaista lähestymistapaa, että teet kaksi funktiota. Yhdessä muunnat minkä tahansa kolminumeroisen luvun oikein ja toisesta käsin kutsut tätä ensin mildardeille, sitten miljoonille, tuhansille ja lopuille. eli juuri kuten Antti neuvoi. Jos miljoonia on enemmän kuin yksi, tulostetaan ensin niiden määrä (sataviisikymmentäkolme) ja sen perään "miljoonaa", ja jos vain yksi, niin tulostetaan suoraan "miljoona". (Jos miljoonia ei ole, niitä ei tietenkään tarvitse tulostaa.) Erikoistapauksena pitää käsitellä pelkkä nolla.
Tein tuollaisen juuri itse, eikä se tosiaan ollut kovin suuri työ. Kannattaa vain miettiä rauhassa paperin kanssa, miten luvut helpoiten rakentuvat.
Jep, pitää koklata tota. Itellä on vaan se ongelma, että ei ikinä malta oikein suunnitella, vaan aina on hirveet poltot päästä suoraan koodaa. :) Sittenhän se menee miten se menee.. Tulee turhaa työtä tehtyä vaan.
Aihe on jo aika vanha, joten et voi enää vastata siihen.