tiistai 13. tammikuuta 2009

Luento 2 - Lausekkeista ja vähän muustakin

Toinen luento keskittyi erityisesti lausekkeisiin ja niiden merkitykseen ohjelmointikielissä. Lausekkeita voidaan pitää kaikkien ohjelmointikielien lähtökohtana. Kullakin kielellä on formaali määritelmänsä, lausekkein kuvattu kielioppi, joka määrää kielen syntaksin ja sen tukemat ominaisuudet.

Useat kielet käyttävät samankaltaista, luontevaa syntaksia peruslaskutoimitusten kuvaamiseen. Lisäksi sijoitukseen käytettävä merkki "=" on pääosin vakiintunut, joskin poikkeuksia Pascal esimerkkinä löytyy. Olen samaa mieltä luennoitsijan kanssa siitä, että merkintä ei ole erityisen onnistunut, koska se sotii useimpien henkilöiden matematiikasta peräisin olevaa intuitiota vastaan. Tämän vuoksi Pascalin käyttämä käsittääkseni nimenomaan matematiikasta peräisin oleva ":=" tapa merkitä sijoitusta tuntuu luonnollisemmalta valinnalta.

Toinen seikka, jonka kanssa "=" notaatio saattaa aiheuttaa ongelmia on yhtäsuuruusoperaattori, jonka avulla voidaan testata esimerkiksi ovatko annetut arvot samoja. Koska "=" varaa sijoituksen, yhtäsuuruuden testaamiseen käytetään tavallisesti "==" merkintää.

Valitut tavat merkitä asioita lienevät periytyneet pitkälti vanhemmista kielistä ja siten vakiintuneet "de facto" standardeiksi. Jos kukin kieli toteuttaisi oman tapansa ilmaista esimerkiksi juurikin sijoitusta, tietäisi se enemmän päänvaivaa ohjelmoijille. Toisaalta turhan eksentrinen kieli jääneekin miltei automaattisesti marginaali-ilmiöksi.

Lausekkeiden lisäksi on oleellista muistaa, että kielen kieliopin kuvaamiseen tarvitaan muutakin. Kielioppi voidaan antaa lausekkeiden muodostamana kuvauksena, joka tarvittavissa kohdin sisältää sisäkkäiset lausekkeet sallivan rekursion, eli kutsun lausekkeeseen itseensä, sekä terminaattorit eli tavallisesti esimerkiksi lukuarvot ja muut vastaavat. Tällä abstraktilla kuvauksella pääsee alkuun. Ongelmiin päädytään, kun joudutaan miettimään operaatioiden järjestystä. Esimerkiksi kertolasku on matematiikassa "arvokkaampi" operaatio kuin summaus- tai vähennysoperaatio.

Operaatioiden arvojärjestykseen voidaan ottaa kantaa itse kuvauksessa. Tämä onnistuu määrittelemällä kielioppi oikealla tavoin. Tässä tapauksessa puhutaan konkreetista kieliopista. Perusajatuksena on osittaa abstrakti kuvaus pienempiin alijoukkoihin, joissa kussakin rekursio on rajattu siten, että kaksiosaisen operaation alkuosa voi kutsua alijoukkoa itseään ja loppuosa toista alijoukkoa arvokkaampaa joukkoa. Tällä tavoin operaatioiden välinen arvojärjestys saadaan rakennettua itse kielioppiin. Olennaista kieliopin konkreettisessa esityksessä on myös se, että tällaisen kieliopin avulla voidaan jäsentää, mikä ei abstraktia kielioppia käyttäen ole mahdollista! Abstraktia kielioppia onkin kenties helpompi ajatella apuvälineenä, jonka avulla konkreetti kielioppi voidaan johtaa.

Arvojärjestyksen määrittely ei välttämättä riitä kaikissa tapauksissa. On mahdollista, että kieliopin avulla muodostettu lauseke sisältää vierekkäisiä, samanarvoisia operaattoreita. Tässä tapauksessa puhutaan assosiatiivisuudesta. Ohjelmointikielessä voidaan määritellä, miten kyseinen tapaus käsitellään. Voi olla esimerkiksi mielekästä, että laskennan tarkkuuden kannalta kieli pyrkii arvioimaan lausekkeita siten, että lopputulos on numeerisesti mahdollisimman tarkka. Toisaalta voisi kuvitella, että joissain tapauksissa pienimuotoista laskentaa voisi harrastaa jopa rinnakkain. Tämä kuitenkin menee jo turhan tekniselle tasolle. :)

On huomattava, että perinteinen peruskoulumatematiikasta tuttu notaatio kirjoittaa laskuja ei ole ainoa mielekäs tapa. Vaihtoehtoina esiteltiin puolalainen notaatio ja sen variantit. Perusajatuksena on, että operaattoreita ja niille syötettäviä arvoja arvioidaan hieman eri tavalla. Esimerkiksi operaattorin voidaan ilmaista itse laskun alussa ja käytetyt arvot vasta operaattoreiden jälkeen. Käsittääkseni kyseistä tapaa syöttää tietoa on käytetty ainakin joissain vanhemmissa laskimissa sen nopeuden vuoksi. Ei siis ole välttämättä syytä luottaa tuttuun ja turvalliseen tapaan vaan toisinaan hieman erilainen notaatio voi olla hyödyllinen. Toisaalta vaihtoehtoisen tavan hyödyntäminen voi olla mielekästä jo ihan oppimisen kannalta, onhan yksinkertainen pinoa hyödyntävä postfix (eli numerot ensin ja operaatiot vasta sitten) notaatiota tukeva tulkki helppoa kirjoittaa. Tätä kautta voi sitten siirtyä hieman haastavampiin ongelmiin.

Abstraktiin kielioppiin palatakseni voisin vielä mainita sen, että abstraktin kieliopin avulla koostettu lauseke voidaan esittää puurakenteena. Puurakenteessa mielenkiintoista on sen yksikäsitteisyys, mitä kieliopin peruskuvauksella ei ole. Tämä johtuu olennaisesti tavasta, jolla puita luetaan. Mielenkiintoisena kuriositeettina puumainen esitystapa (tunnetaan myös nimellä AST, Abstract Syntax Tree eli abstrakti syntaksipuu) muistutti minua eräästä vastaavanlaisesta käytännön ongelmasta.

Graafeja (tavallisesti DAG, Directed Acyclic (eli syklitön) Graph) evaluoitaessa voi olla mielekästä optimoida prosessia siten, että operaatioita edustavia noodeja ryhmitellään taustalla (ts. siten ettei käyttäjä näe sitä) siten, että kukin ryhmä sisältää mahdollisimman monta toisiinsa liittyvää operaatiota, kuten abstrakti syntaksipuu ikään. Yhdistämällä useita operaatioita toisiinsa voidaan säästää huomattavasti aikaa, koska muuten atomisia operaatioita voidaan ajatella komposiittina, joka voidaan prosessoida sellaisenaan. Tämä onkin eräs osoitus siitä, että kieliopeista tutuista menetelmistä on hyötyä myös niiden ulkopuolella.

Kenties eräs olennaisimpia asioita, jonka omaksuin, oli matematiikan tukema havainto siitä, että kaikki kielet ovat sukua toisilleen. Ainakin erittäin korkealla, initiaalialgebrojen tasolla. Tarkoittaako tämä myös automaattisesti sitä, että millä tahansa kielellä kirjoitettu ohjelma voidaan kääntää toiselle kielelle? Sinänsä ajatus tuntuu hieman absurdilta ottaen huomioon, että kieliä on niin monenlaisia ja ne voivat erota erittäin paljon toisistaan. Kuitenkin ottaen huomioon matemaattisen taustan uskon, että tämä on mahdollista olettaen, että aikaa on käytettävissä rajaton määrä.

Luennon olennaisin anti liittyi nimenomaan kielen kuvaamiseen alkeellisimmalla, eli kieliopin tasolla. Todellisuudessa kieliopit eivät välttämättä ole kovinkaan monimutkaisia. Hyvä esimerkki tästä on Luan syntaksi. Esimerkiksi Pythonin syntaksi on jo aavistuksen monimutkaisempi mutta edelleen varsin luettava. Käytännössä suurin haaste olisikin oman kielen ja syntaksin laatiminen. :)

1 kommentti:

  1. Yhteenvedossa mainittua omaa kieltä tulikin sitten myöhemmin pohdittua demoissa. Näin jälkikäteen ajatellen tämän luennon olennaisin anti oli nimenomaan lausekkeiden abstraktio (AST sekä esitystavat kielioppien avulla).

    Tässä yhteydessä varmaan kannattaa mainita, että eri operaattoreiden symbolit (esim. summauksen "+") kannattaa erottaa selvästi semantiikastaan. Jos haluaisi olla tarkka näille voisi antaa formaalit määritelmänsä. Asiasta olikin puhetta myöhemmillä luennoilla (semanttiset sulkeet, lambdalaskento).

    VastaaPoista