Kuulin, että kovin monien sisäkkäisten luuppien kirjoittaminen ohjelmiin ei ole hyvän tavan mukaista. Tarvitsisin kuitenkin tulostaa kaikki 10-numeroiset luvut, joiden numerot ovat laskevassa järjestyksessä. Voiko tuon tehdä ilman sisäkkäisiä silmukoita? Oma idea kun on seuraava:
for (a1=1;a1
for (a1=1;a1
Vastaukset 31
Vanhimmat
Ite alkaisin tuossa vaiheessa miettimään että miten tuo tehdään rekursiolla.
Jos tarvitaan kymmenen sisäkkäistä silmukkaa, niin
kirjoitetaan kymmenen sisäkkäistä silmukkaa.
Se on erinomaisen yksinkertainen ja ymmärrettävä
tapa, ei siinä mitään rekursiota tarvita.
"Hyvän tavan" mukaista on kirjoittaa sellaista
koodia jonka itse ymmärtää, ja hyvä olisi jos
toisetkin pääsisivät siitä tolkulle.
Rekursioista ei ota selvää pirukaan.
ja jos sillä vältetään 10 sisäkkäistä luuppia, niin tottakai sitä pitää käyttää.
Se että väittää sen olevan jotenkin hankalaa ymmärtää on pelkkää laiskuutta ja laiskuudesta pitäisi päästä koodatessa eroon.
Tehdään kerran kunnolla ja niin että se koodi skaalautuu eikä niin että mennään helpomman kautta.
Ja jos tarvitaan sata sisäkkäistä looppia, kirjoitetaan sata sisäkkäistä looppia. Just.
Itellä on hieman eri käsitys siitä, että mitä tarkoittaa hyvän tavan mukainen koodi.
Aloittajan esimerkki oli yksinkertainen ja muuttujat näytti olevan jopa nimetty hyvin, eli tuossa noi loopit jotenkin vielä menisivät. Mutta aika harvoin tuo on noin yksinkertaista, jolloin rekursio tekisi koodista paljon selkeämpää. Ja koodi tulee lähtökohtaisesti kirjoittaa sellaiseksi, että se on joku muu, joka ylläpitää sitä.
yksi kappaletta kokonaislukuavaruudessa. Eihän niitten lukujen tulostamisesta pidä väkisin sen vaikeampaa tehdä kuin se todellisuudessa on?
Ei tarvi ei ja siksi voikin ihmetellä miksei sitä tee helposti ja siistin näköistä koodia rekursiolla. Mutta jollei osaa, niin ei osaa.
että tehtävässä on tarkoitus tulostaa kaikki sellaiset luvut, joissa jokainen numero on edellistä pienempi eikä ainoastaan lukua 987654321
lukuja, joissa numerot ovat laskevassa järjestyksessä: tehtävän asettaja on aivan selvästi testannut, ymmärtääkö ratkaisua yrittävä tehtäväänsä ensinkään. Oikea ratkaisu on tulostaa ainoastaan luku 987654321, koska myöskään kokonaislukuavaruudessa etunollien käyttö ei tee minkään muunkaan numeroisesta luvusta toisennumeroista.
Täällä sinänsä toki ansiokkaista atk-toimistanne nähty tehtävänratkaisu oli kuitenkin it-maailmalle täysin tyypillinen siten, että kehitellään kaikkea hienoa ja osin nerokastakin, jolla ei kuitenkaan ole mitään tekemistä alkuperäisen tehtävän kanssa. Juuri tästä syystä kaiken atk-maailman it-projektit jäävät pahasti aikatauluistaan ja tulevat hirvittävän kalliiksi.
9876543210? Luvun lopussa olevilla nollilla on väliä. Olishan tuo kyl pitänyt huomata et mitä siinä itse kysymyksessä kysyttiin mutta aloittaja kyllä kysyi täysin eri asiaa.
atk-virheitten myöntäminen myöskään ei ole it-maailmassa kovin herkästi nähty ilmiö, kuten ohjelmointiesimerkkinnekin loistavasti osoittaa.
Mikäli atk-virheet myönnettäisiin heti, kun ne huomataan tai kun joku siitä it-projektista sivullinen, kuten esimerkiksi sen tilaaja eli niitten atk-viulujen maksaja alkaa ihmetellä, että: "mitä ihmettä ne nyt taaskin on keksineet", niin syntyisi silmittömiä atk-säästöjä, mutta kun ei niin ei: aina yritetään selitellä ilmiselvät virheet parhainpäin, vaikka sen virheen heti myöntämällä ja korjaamalla sen it-projektin aikataulu ei venyisi ja paukkuisi läheskään niin paljon kuin tyhjäntoimittamalla siinä saadun atk-tehtävän sivusta.
Kyllä asiakkaankin pitäisi olla sen verran kiinnostunut ostamasta tuotteestaan, että haluaa ottaa osaa sen kehitykseen. Turhan usein asiakkaat odottavat että koodarit ovat jotain ajatusten lukijoita ja sit pettyvät ku me ei osatakkaan.
Voisiko ketjun aloittaja täsmentää, mitä hän oikein
on hakemassa:
1) lukuja väliltä 9 999 999 999 - 0 000 000 000?
vai
2) lukua 9 876 543 210?
Tuohon viimemainittuunhan ei luuppeja eikä rekursiota
tarvita.
Jos taas on kyse ykköstapauksesta, niin voimme kukin
laatia omat versiomme ja kilpailla siitä kenellä on
typerin ratkaisu.
silmukoita.
Ketjun avaaja on ilmeisesti menettänyt kiinnostuksensa asiaan,
tässä silti tämä "projekti" sellaisena kuin minä sen ymmärsin.
Tämähän vei koneen resurssit tyystin, ohjelman suoritusaika
oli 0.16 sekuntia 15 vuotta vanhalla tulkkaavalla Qbasicilla,
1300 MHz Celeronilla. (Nopeampiakin löytyy tarvittaessa.)
QBasic löytyy Windows98:n asennuslevyltä ja muistaakseni
myös OS/2 Warp:in asennuslevyiltä (peräti suomenkielisillä
ohjeilla) sekä "tuhansista" paikoista internetistä.
VisualBasic6:lle sovellettuna ja käännettynä kaikilla
"nopeusoptimoinneilla" menee aika negatiiviseksi,
"muokkausympärististössä" ajettuna aika on 0.015 sekuntia.
Mainittakoon vielä, että jos luupit pyörivät yhdeksästä
nollaan kestää ohjelman suoritus lähes minuutin VB6:en
käännetyllä ohjelmalla, ja kaikilla tulkkaavilla kielillä
taitaa "aika tulla pitkäksi" (myös QBasicilla).
Lukuja löytyi 92378 tai 92377 riippuen siitä pyöritetäänkö
silmukka1%:n nollaan vai ykköseen.
Tässä tämä surkea listaus surkealla Basic-kielellä:
' "Muuttujatyyppien" selitykset
' # = desimaalilukumuuttuja, kaksoistarkkuus
' % = kokonaislukumuuttuja, 2 byteä"
' ! = desimaalilukumuuttuja, perustarkkuus
laskuri# = 0
aika! = Timer
For silmukka1% = 9 To 0 Step -1
For silmukka2% = silmukka1% To 0 Step -1
For silmukka3% = silmukka2% To 0 Step -1
For silmukka4% = silmukka3% To 0 Step -1
For silmukka5% = silmukka4% To 0 Step -1
For silmukka6% = silmukka5% To 0 Step -1
For silmukka7% = silmukka6% To 0 Step -1
For silmukka8% = silmukka7% To 0 Step -1
For silmukka9% = silmukka8% To 0 Step -1
For silmukka0% = silmukka9% To 0 Step -1
laskuri# = laskuri# + 1
Next silmukka0%
Next silmukka9%
Next silmukka8%
Next silmukka7%
Next silmukka6%
Next silmukka5%
Next silmukka4%
Next silmukka3%
Next silmukka2%
Next silmukka1%
aika! = Timer - aika!
Print Str$(aika!) + " " + Str$(laskuri#)
End
Ps. Saisiko nyt joltain ammattilaiselta toimivan "rekursion".
>> Ps. Saisiko nyt joltain ammattilaiselta toimivan "rekursion".
Jos katsot ketjua alaspäin niin löydät rekursiivisen ratkaisun, mikä toki on aika helppo algoritmi sekin. Vai eikö se toiminut? Toki rekursiivinen toteutus tekee samat laskut kuin kymmenellä sisäkkäisellä luupillakin toteutettu.
>> Ps. Saisiko nyt joltain ammattilaiselta toimivan "rekursion".
Olit oikeassa, testaamattomassa pseudokoodi-rekursiossani oli pieni bugi, mutta alla olevan pitäisi toimia paremmin. Muuttamalla luvun 10 alla vaikka 100:ksi saa tulostettua 100-numeroiset vastaavat luvut. Kuinkas tämä tehdään sinun koodissasi?
#include
#include
void tulostus(std::string prefix = "", int max = 9, int n = 10) {
if (n == 0) {
std::cout
konekieliset sisäkkäiset loopit rekursioksi?
Täytyy löytyä joku YLEINEN konsepti - etemkin JOS 'osaa'?
>> konekieliset sisäkkäiset loopit rekursioksi?
>>
>> Täytyy löytyä joku YLEINEN konsepti - etemkin JOS 'osaa'?
Niin siis totta kai tämä onnistuu, ihan samalla periaatteella kuin ohjelmointikielten kääntäjät tuottavat assembler-koodin rekursiivisille funktioille. Toki se rekursio formaalisti muutetaan pino-rekisteri-talletuksiksi mutta ihan rekursiolta se sielläkin kooditasolla näyttää. Millä assemblerilla saisi olla esimerkki? Käykö m68k?
esimerkki riittää ja sehän on kielestä riippumaton.
:)
Veikkaan että useammat täällä eivät edes tiedä rekursion ja silmukan eroa, puhumattakaan konekielen tasolla, jauhatte kaikki samaa skeidaa, toiseksi tehtävän anto on päin p:tä, miettikää 2x ennenkuin jauhatte sontaa täällä, tämä ei ole kaikille ketjussa vaan joillekkin, rupes vaan ottaa nuppiin, kiitos ja anteeksi!
No yksinkertaisimmillaan voidaan rekursio ymmärtää
"itseään kutsuvaksi rutiiniksi", ja kaunistahan se
on ja siistiä.
Olen kuitenkin edelleen sitä mieltä, että tämänkin
palstan lukijoista 99% menee "juntturaan" joutuessaan
tutkimaan toisten tekemiä "itseään kutsuvia ohjelmia".
Olisi myös erittäin mukava kuulla, miten ripeästi käy
sadan kymmenarvoisen luvun pyöritys. Ja onko luvulla
muuta tarkoitusta kuin osallistua "maailman suurin
luku"-keskusteluun, joka näyttää kiinnostavimmalta
asialta tuolla matematiikkapalstalla.
Alkuperäinen kysyjä pohti onko "hyvän tavan mukaista"
käyttää sisäkkäisiä silmukoita ja voiko asian hoitaa
jotenkin toisin.
Ilman sisäkkäisiä silmukoita ei ohjelmoinnista tule
mitään, ja jos oikein tarkkaan katsotaan niin eiköhän
monet rekursiot myös toteuta "sisäkkäisiä silmukoita".
> No yksinkertaisimmillaan voidaan rekursio ymmärtää
> "itseään kutsuvaksi rutiiniksi", ja kaunistahan se
> on ja siistiä.
>
> Olen kuitenkin edelleen sitä mieltä, että tämänkin
> palstan lukijoista 99% menee "juntturaan" joutuessaan
> tutkimaan toisten tekemiä "itseään kutsuvia ohjelmia".
Ainakin silloin kun vielä opiskelin, rekursio kuului ihan tietotekniikan perusjuttuihin ja jos menee "juntturaan" rekursiosta on se sama kuin menisi matematiikassa "juntturaan" derivoinnista. Tai kertolaskusta.
> Alkuperäinen kysyjä pohti onko "hyvän tavan mukaista"
> käyttää sisäkkäisiä silmukoita ja voiko asian hoitaa
> jotenkin toisin.
> Ilman sisäkkäisiä silmukoita ei ohjelmoinnista tule
> mitään, ja jos oikein tarkkaan katsotaan niin eiköhän
> monet rekursiot myös toteuta "sisäkkäisiä silmukoita".
Vastaus on, että 10 sisäkkäistä silmukkaa on huonoa ohjelmointityyliä. Erityisesti jos sisäkkäisten silmukoiden määrä on riippuvainen tuosta parametrista 10. Tällöin kannattaa abstrahoida ongelmaa hieman ja miettiä algoritmi uudestaan sillä tavalla että se on paremmin laajennettavissa ja "elegantimpi". Tässä tapauksessa hieman eri filosofialla toimiva rekursio tai saman asian toteuttava iteratiivinen ratkaisu on parempi.
Huoh... näitä viestien tasoa... ei ihme että ei jaksa lukea tätä palstaa.
Sisäkkäisten silmukoitten kirjoittaminen ei ole hyvän tavan mukaista. Miksi?
Koska jos lopputuloksena on kauhea määrä sisäkkäisiä silmukoita niin luultavasti ajattelemme ongelmaa liian monimutkaisesti ja ohjelma toimii hitaasti ja syö kaikki resurssit koneesta.
Kuinka paljon 10 numeroisia lukuja on? Hei nehän on luvut 9999999999-1000000000. Sitten ne pitäisi tulostaa laskevassa järjestyksessä. Elikkä aloitetaan isoimmaista luvusta ja edetään kohti pienintä lukua.
Seuraava mietittävä asia on että mikä tietotyyppi sopii luvuille jotka ovat suuruusluokkaa 9999999999-1000000000.
Tähän ei ole oikeata vastausta. Mutta jos nyt oletetaan että eletään turvallisesti vielä 32 bittisessä ympäristössä niin tietotyyppinä toimii "long long" tai "unsigned long long" maku kysymys. Mutta unsigned long on liian pieni 2^32 = 4 294 967 296.
Sitten täytyy vielä tietää että käytettäessä suuria lukuja jotka ylittävät perus int tietotyypin lukualueen on käytettävä jälki liitettä joka on tässä tapauksessa LL. Koska kaikki määritellyt vakiot ovat int tyyppisiä C/C kielissä ellei niitä erikseen määritellä tai tapahdu ajonaikaista tyyppimuunosta. Esim. visual studio antaa varoituksen jos tuon LL loppumääritteen unohtaa.
Sitten voidaankin kirjoittaa se silmukka joka tulostaa ne luvut.
for(long long c=9999999999LL; c>999999999LL; c--)
{
cout
Älkää nyt hyvät ihmiset opettako aloittelijaa pahoille tavoille ja käyttämään jotain ylikuormitettuja operaattoreita. Nämähän on näitä ansoja mitä kieleen on tehty tarkoituksella, että pöllömmät ei osaisi ohjelmoida kunnolla.
http://www.google.fi/search?hl=fi&q=big integer c &btnG=Google-haku&meta=&rlz=1R2GGLR_fiFI329
mutta silti on sanottava, että tässä käyty keskustelu ja ratkaisumallinnus ei ole tilaajan toiveiden mukainen edes lähtökohdiltaan, koska ratkaisutiimi ei ole lainkaan ymmärtänyt saamaansa tehtävää ja kykenemättä myöntämään tätä, ovat alkaneet esitellä toinen toisilleen ohjelmointitaitojaan, jotka eivät kuitenkaan riitä alkuperäisen tehtävän ratkaisuun, joka oli, kuten muistamme heille liian triviaali.
mallinsin Maailmankaikkeutta ja heti kättelyssä kävi ilmi, että siihan saakka tuntemistani tietokoneistoluvuista suurimmat ja pienimmät olivat aivan liian piniä ja suuria tarkoitukseeni, joten aikani etsittyäni pääsin vielä suurempien ja pienempien tietokoneitolukujen jäljille ja Maailmankaikkeuden mallistani tuli hyvin tarkka. Vasta Hubblen teleskoopin viimeisen silmäleikkauksen jälkeen olemme saaneet nähdäksemme yhtä yksityiskohtaisia kuvia Maailmankaikkeudestamme...hetkinen...noniin...
noin suuria lukuja. Eikö voisi lineaarien sijaan käyttää desibelejä, jotka ovat logartimien eräs joukko, johon kuvattuna äärettömän suuri ja hävyttömän pieni saadaan näkyviin samassa kuvassa kokonaisuuksissaan ritirinnan?
Niin, tai sitten voisi keksiä jonkun lukujärjestelmän missä osa luvusta esitetään desibeleinä tai yleisemmin logaritmisena ja osa lineaarisesti! Ainiin, mutta niinhän liukuluvut tietokoneessa toimivatkin...
Eli ilmeisesti haluat tulostaa lukuja kuten:
1000000000
1100000000
2100000000
...
Yksi tapa on sellainen, että teet itse sellaisen laskurin, joka alkaa luvusta 1000000000 ja lisää joka kierroksella ykkösen yhteen numeroon seuraavalla logiikalla:
- Oikealta lähtien etsi ensimmäinen numero, jonka vasemmalla puolella on suurempi numero. Tähän numeroon lisätään 1 ja kaikki siitä oikealla olevat nollataan.
- Jos tällaista ei ole, lisätään vasemmanpuoleiseen numeroon 1 ja nollataan kaikki muut numerot.
- Jos edellisessä vasemmanpuoleinen numero olikin jo 9, homma on valmis.
Tämän voi toteuttaa vaikka 10-pituisena taulukkona tai 10-pituisena stringinä, jolloin voidaan käyttää suoraan ASCII-arvoja ja tulostus on helpompaa. Esimerkiksi 2:lla numerolla saadaan:
10
11
20
21
22
30
31
32
33
...
Toinen tapa on tehdä sama asia rekursiolla jotenkin näin (en testannut ja ainakin 0000000000 pitää poistaa tulostuksesta):
function tulostus(max=9,n=10)
if n = 0
tulosta rivinvaihto
return
endif
for i = 0 to max
tulosta i
tulostus(i,n-1)
endfor
endfunction
Tuo edellinen ratkaisu on sama asia, mutta rekursio on eliminoitu (mikä on hyvä juttu muualla paitsi scheme-harjoitustehtävissä).
>Tämän voi toteuttaa vaikka 10-pituisena taulukkona
|$l = 10;
|split '', '0' x $l;
|$_[0] ;
|f: print join('', @_), "\n";
|for ($d = $l-1; $d; $d--) {
| if ($_[$d-1] - $_[$d]) {
| $_[$d] ;
| goto g
| }
|}
|if (9 - $_[0] ) {
| g: for ($d 1 .. $l-1) {
| $_[$_] = 0
| }
| goto f
|}
Todella selkeä esimerkki, varmasti auttaa kaikkia paljon.