Tämänkertainen teema on tärkein, sillä melkein kaikki muut perustuvat sille jollakin tapaa. Periaatteessa opettelemme, miten ohjelmia yleistetään niin, että niistä tulee monikäyttöisempiä, ja yhdistellään niin, että selviämme vähemmällä kirjoitusvaivalla. Käytännössä opettelemme tekemään komentoja, joille voi antaa syötettä. Lisäksi opimme siinä sivussa muokkaamaan ja muuttelemaan aiemmin tekemiämme ohjelmia.
Tässä teemassa opetettujen asioiden koko hyödyllisyys paljastuu vasta pikku hiljaa myöhempien teemojen myötä. Voimme kuitenkin jo tässä vaiheessa tehdä paljon hauskoja ja ihan hyödyllisiäkin asioita.
Tarvitsemme tässä teemassa joitain valmiiksi tehtyjä ohjelmia. Voit käyttää jotain aiemmin tallettamaasi tai kirjoittaa muutaman ohjelman (kuten neliö ja ympyrä) ensin. Itse aion käyttää teeman 3 määrittelyitä lähtökohtana:
? lataa "harkat3
Teitpä uudet ohjelmat tai latasitpa vanhoja, sinulla pitäisi nyt olla joitain määrittelyitä olemassa. Ne saa listatuksi "ohjelmat"-komennolla. Minulla lista on tällainen:
? ohjelmat miten kaario miten kaariv miten lehti miten neliö miten nuoli miten orvokki miten pää miten raaja miten seppele miten silmät miten sormet miten ukkeli miten vartalo miten ympyrä
Yhdestä ohjelmasta saa tietää määrittelyn komennolla "näytä". Kuten lataa- ja tallenna-komennoissa, näytä-komennon syöte alkaa lainausmerkillä. Miksi, muuten? Ilman lainausmerkkiä kirjoitetut sanat ovat komentoja ja jos kirjoitat neliö, logo tekee sen (siis neliön). Lainausmerkin kanssa kirjoitetut taas ovat nimiä tai sanoja ja jos kirjoitat "neliö, Logo tekee jotain sille (siis neliölle). Tässä tapauksessa Logo tekee neliölle sen, että näyttää sen määritelmän.
? näytä "neliö miten neliö toi 4 [et 100 oi 90] valmis
Jos haluamme muokata tätä määritelmää, voimme käyttää komentoa "muokkaa" tai sen lyhennettä "muo". Kokeile:
? muo "neliö
Voit nyt tehdä neliöön haluamiasi muutoksia (sinulla pitäisi olla näytölläsi nyt jonkinlainen muokkausohjelma, jossa voit muutella määritelmää). Lopuksi sulje muokkausohjelma ja tallenna muutokset. Muuta neliön määritelmä vaikkapa seuraavanlaiseksi:
miten avoin_neliö toi 4 [et 100 oi 88] valmis
Jos et muuta komennon nimeä, uusi määritelmä korvaa vanhan; jos muutat komennon nimeä (kuten yllä neliö muutettiin muotoon avoin_neliö), vanha määritelmä säilyy ja syntyy myös uusi. Käytä ensimmäistä tapaa, kun haluat korjata ohjelmiasi, ja toista tapaa, jos haluat tehdä uusia komentoja vanhojen pohjalta.
Muokattua ohjelmaa kannattaa tietysti kokeilla:
? näytä "avoin_neliö miten avoin_neliö toi 4 [et 100 oi 88] valmis ? avoin_neliö
Katsele ja muokkaa nyt joitain omia ohjelmiasi.
Nyt tulee tämän teeman sydän. Tähän mennessä määrittelemämme ohjelmat ovat olleet oikeasti käteviä, mutta aina vähän mielivaltaisia. Esimerkiksi määrittelemämme neliö, ympyrä ja kaaret ovat juuri tietynlaisia: aina saman kokoisia, muotoisia ja pituisia. Olisi huiman kätevää, jos pystyisimme tekemään samalla komennolla vähän useammanlaisia neliöitä. Ja sitä varten tarvitsemme komentoja, joille voi antaa syötettä, koska syötteellä voi kertoa neliölle, millaisen neliön siitä haluaa, samaan tapaan kuin kerromme "eteen"-komennolle, kuinka paljon haluamme mennä eteenpäin. Nyt alamme tehdä näitä ruokittavia komentoja.
Neliöllä on aivan ilmeinen ominaisuus, jonka kannattaa olla muutettavissa, nimittäin koko. Haluamme muuttaa neliön sellaiseksi, että sille voi kertoa neliön sivun pituuden, niin että "neliö 100" tekee yhden kokoisen neliön, "neliö 50" puolet pienemmän ja "neliö 10" pikkuriikkisen neliön.
Alamme taas muokata neliötä. Tästä lähdetään:
? muo "neliö
Ja näytölläsi pitäisi olla:
miten neliö toi 4 [et 100 oi 90] valmis
Muuta määritelmä tällaiseksi:
miten neliö :koko toi 4 [et 100 oi 90] valmis
Neliölle tulevat syötteet luetellaan miten-komennossa komennon nimen jälkeen. Neliö-sanan perään lisätty ":koko" tarkoittaa siis, että neliö-komento ottaa yhden syötteen, jonka nimi on ":koko". Kaikkien syötteiden nimet alkavat kaksoispisteellä. Kaksoispiste tarkoittaa: "tämä ei ole mikään tietty luku, vaan pelkkä paikanpitäjä, jonka tilalle tulee myöhemmin jokin oikea luku". Tällaista paikanpitäjää sanotaan muuttujaksi siksi, että sen tilalle voi tulla erilaisia lukuja.
Voit nyt tallentaa uuden määritelmän. Kokeillaan sitä:
? neliö neliö haluaa enemmän syötteitä ? neliö 10 ? oi 10 ? neliö 20
Muutos on siinä mielessä onnistunut, että neliö haluaa syötteitä, mutta harmillista kyllä neliö ei vielä käytä syötettään mihinkään (vaan siitä tulee aina samanlainen neliö). Korjataan tilanne:
? muo "neliö
Muuta nyt määritelmä tähän muotoon (eli muuta et-komennon syöte niin, ettei se ole enää "100" vaan ":koko"):
miten neliö :koko toi 4 [et :koko oi 90] valmis
Tallenna, sulje ja kokeile.
? uusi ? neliö 100 ? neliö 50 ? neliö 20
Jee, se toimii!
Miksi se toimii?
Kuten sanoin, ":koko" tarkoittaa jotain lukua, joka kerrotaan myöhemmin. Kun sanon "neliö 20", Logo ajattelee: "Ahaa, :koko on nyt 20." Kun Logo tekee neliötä, se tietää, että :koko on 20, joten on kuin olisimme kirjoittaneet:
toi 4 [et 20 oi 90]
Kun taas sanon "neliö 50", :koko onkin 50, joten Logo tekee neliön kuin olisimme kirjoittaneet:
toi 4 [et 50 oi 90]
Kätevää, eikö?
Komennosta voi korvata ihan minkä tahansa luvun (ja paljon muutakin, kuten myöhemmin näemme) muuttujalla, joka annetaan komennolle syötteeksi. Muutetaan esimerkiksi avoin_neliö näin:
? muo "avoin_neliö
miten avoin_neliö :kulma toi 4 [et 100 oi :kulma] valmis
Nyt komennolla saa eri tavalla hajalla olevia neliöitä:
? uusi ? avoin_neliö 88 ? keskelle ? avoin_neliö 80 ? keskelle ? avoin_neliö 97
Tällaista uutta komentoa on tietysti hauskaa testata eri syötteillä. Joskus kannattaa jättää Logon tehtäväksi tuottaa erilaisia syötteitä (tietokoneethan ovat hyviä toistamisessa):
? uusi ? toi 10 [neliö kerta*10] ? toi 10 [neliö kerta*10 oi 20]
Kokeile nyt tehdä muokkaamalla tai suoraan määrittelemällä esimerkiksi ympyrä tai kolmio, joka osaa ottaa syötettä, ja testaa uusi komentosi.
Usein meillä on määrittelyitä, joissa on useampia toisiinsa sovitettuja lukuja. Ajatellaan esimerkiksi, että haluamme tehdä vinoneliön. Sen määritelmä voisi olla tällainen:
? miten vinoneliö > toista 2 [et 50 oi 60 et 50 oi 120] > valmis
Vinoneliö koostuu neljästä käännöksestä, joiden summan tulee olla 360 astetta (muuten lennokki ei palaudu alkuasentoon eikä kuvio voi olla vinoneliö). Niinpä käännökset "oi 60" ja "oi 120" riippuvat toisistaan siten, että niiden summan pitää olla 180 astetta. Kun vinoneliöön lisätään käännöskulman antava syöte, pitää myös säilyttää tämä johdonmukainen riippuvuus (tähän on monta tapaa, tässä yksi):
? muo "vinoneliö
miten vinoneliö :kulma toista 2 [et 50 oi :kulma et 50 oi 180-:kulma] valmis
Nyt määrittelyyn on kirjoitettu auki se logiikka, josta astekulmat 60 ja 120 syntyivät: mikä tahansa määrä ensin käännytäänkin, pitää seuraavaksi kääntyä 180 miinus tuo määrä, koska muuten käännöksistä ei tule yhteensä 180 astetta. Muuttujaa :kulma käytetään kaksi kertaa siten, että sen aiheuttamat käännökset sovitetaan yhteen. Määritelmällä pystyy tekemään eri verran vinoja neliöitä ("liuu"-komento on yksinkertainen yhdistelmä oi-, hyps- ja va-komentoja):
? uusi ? toista 10 [vinoneliö kerta*10 liuu 30]
Kun jollekin kuviolle halutaan lisätä syötteeksi koko, törmätään usein hyvin samanlaiseen ongelmaan. Usein kuvio sisältää monia eri pituuksia, jotka seuraavat erilaisilla kaavoilla toisistaan. Tarkastellaan vaikkapa "nuoli"-määrittelyä:
? näytä "nuoli miten nuoli et 100 va 135 et 50 va 135 et 71 va 135 et 50 valmis
Mistä pituudet 100, 50, 71 ja 50 oikein tulevat? Ne on määritelty toistensa perusteella niin, että nuolen mittasuhteet näyttävät järkeviltä. Erityisesti luvut 50, 71 ja 50 (sekä käännösmäärä 135 astetta) on laskettu siten, että nuoli sulkeutuu nätisti. (Nuolen kärki on neliön puolikas, joten halkaisijan pituus on laskettu Pythagoraan kaavalla. Jos et tunne sitä, älä välitä, sillä vielä ei tarvitsekaan.)
Jos haluamme säilyttää nuolen mittasuhteet, kaikkia pituuksia pitää muuttaa samassa suhteessa. Voimme esimerkiksi valita "peruspituudeksi" 100 ja laskea kaikki muut pituudet sen perusteella (50 on 100/2 ja 71 on 100*0.71):
? muo "nuoli
miten nuoli :koko et :koko va 135 et :koko / 2 va 135 et :koko * 0.71 va 135 et :koko / 2 valmis
Tämä ainakin selvästi toimii:
? toista 10 [nuoli kerta*10]
Toisaalta määrittelyssä ollut luku 100 (nuolen pituus) ei riipu niin olennaisesti muista luvuista, jotka määrittävät nuolen pään kokoa. Ehkä haluaisimmekin jättää jommankumman erilleen, joko näin:
miten nuoli :leveys et 100 va 135 et :leveys / 2 va 135 et :leveys * 0.71 va 135 et :leveys / 2 valmis
Tai sitten näin:
miten nuoli :pituus et :pituus va 135 et 50 va 135 et 71 va 135 et 50 valmis
Jos haluaa lisätä jollekin kuviolle koko-syötteen suoraviivaisesti, kaikkein helpoin tapa on kuitenkin vain raa'asti kertoa kaikki pituudet koko-muuttujalla. Nimittäin kun kaikki pituudet kerrotaan samalla numerolla, pituuksien suhteet säilyvät, jolloin kuvion muoto säilyy.
Tehdään vaikkapa tällainen A-kirjain:
? miten akirjain > oi 30 > et 100 > oi 120 > et 100 > oi 180 > et 50 > va 60 > et 50 > valmis
Nyt, jos haluamme tehdä vaihtelevan kokoisia A-kirjaimia, helpoin tapa on muokata ohjelmaa näin:
? muo "akirjain
miten akirjain :koko oi 30 et 100 * :koko oi 120 et 100 * :koko oi 180 et 50 * :koko va 60 et 50 * :koko valmis
Nyt "akirjain 1" tuottaa normaalikokoisen A-kirjaimen, "akirjain 2" kaksi kertaa isomman ja "akirjain 1/4" neljä kertaa pienemmän.
Muuta nyt joitain ohjelmiasi sellaisiksi, että niille annetaan mittoja syötteeksi.
Ehkä kaikkein "oikein" ratkaisu on lisätä nuolelle sekä leveys että pituus erikseen syötteiksi, koska silloin nuoli-ohjelmalla voi tehdä kaikenlaisia nuolia.
? muo "nuoli
miten nuoli :pituus :leveys et :pituus va 135 et :leveys / 2 va 135 et :leveys * 0.71 va 135 et :leveys / 2 valmis
Miten-riville voi siis lisätä kuinka monta syötettä haluaa. Jos nyt esimerkiksi annamme komennon "nuoli 100 50", niin :pituus on nuolta piirrettäessä 100 ja :leveys on 50. Jos taas sanomme "nuoli 30 35", niin :pituus on 30 ja :leveys 35. Logo sitten asettelee nämä luvut paikoilleen.
Nyt ainakin saamme aikaan vaikka minkälaisia nuolia. Seuraavassa nuoli-komento ottaa siis kaksi syötettä, joista toinen on "130-kerta" ja toinen on "kerta/2":
? piiloon ? toista 100 [uusi va kerta nuoli 130-kerta kerta/2 odota 1]
Malliesimerkki ohjelmasta, joka vaatii monta syötettä, on kaari. Kaarilla on sekä pituus että astekulma, jonka ne kääntyvät. Tarkastellaan kaario:n määritelmää:
? näytä "kaario miten kaario toi 90 [et 1 oi 1] valmis
Tässä kaaren pituus on 90 askelta, sillä lennokki menee eteenpäin yhden askeleen kerrallaan 90 kertaa. Samoin kaaren käännös on 90 astetta, koska siinä käännytään yksi aste kerrallaan 90 kertaa.
Lisätään kaariolle ensin vaikkapa :pituus-syöte:
? muo "kaario
miten kaario :pituus toi :pituus [et 1 oi 1] valmis
Hienoa, nyt saamme haluamamme mittaisia kaaria:
? toista 10 [kaario 10*kerta va 30]
Mutta nyt :pituus vaikuttaa myös siihen, kuinka paljon kaari kääntyy. Jotta vaikutus saataisiin kumotuksi, kääntymismäärä käskyssä "oi 1" pitää olla jokin luku jaettuna toistokertojen määrällä. Silloin käännymme yhteensä saman verran riippumatta siitä, kuinka monta kertaa menemme eteenpäin.
? muo "kaario
miten kaario :pituus toi :pituus [et 1 oi 90/:pituus] valmis
Mahtavaa, nyt pystymme tekemään aina 90 astetta kääntyviä kaaria riippumatta siitä, kuinka pitkiä ne ovat!
? uusi ? toista 10 [kaario 10*kerta oi 90]
Lopuksi voimme tietysti korvata kiinteän 90 asteen käännöksen jollain muulla luvulla, joka annetaan syötteeksi:
? muo "kaario
miten kaario :pituus :kulma toi :pituus [et 1 oi :kulma/:pituus] valmis
Nyt voimme muutella sekä kaaren pituutta kääntymismäärästä riippumatta että kääntymismäärää pituudesta riippumatta.
? toista 200 [uusi kaario kerta 200 * sin 5 * kerta odota 1]
Kokeile nyt tehdä esimerkiksi nuoli, jossa pystyy muuttamaan sekä nuolen kokoa että nuolen pään terävyyttä.
TODO
Pikalinkit: