Tehtävä. Tämä tehtävä on aineistotehtävä, jossa esitetään ensin tehtävän teoria. Sen jälkeen esitetään neljä kysymystä, joissa tätä teoriaa pitää soveltaa. Mitään aikaisempaa tehtävän aihepiirin tuntemusta ei vastaajalta oleteta. Eräs yksinkertaisimmista laskennan malleista on niin sanottu äärellinen automaatti. Tällainen laite lukee syöttöä ja muuttaa tilaansa sen mukaisesti. Syötteen ajatellaan tässä koostuvan /-jonosta, toisin sanoen bittijonosta. Itse automaatti on helpointa esittää tilojen ja tilasiirtymien avulla. lla on kuva äärellisestä automaatista, jonka avulla voidaan testata onko syötön -bittien määrä parillinen. utomaatissa on kaksi tilaa, ja, ja kaaret kuvaavat tilasiirtymiä. Kaariin on merkitty millä bitillä ( tai ) siirtymä tehdään. Esimerkiksi, jos automaatti on tilassa ja seuraava syöttöbitti on, niin pysytään tilassa. Syötteellä puolestaan siirrytään tilasta tilaan. Vastaavasti tilasta siirrytään -bitillä tilaan ja -bitillä pysytään tilassa. Tilat luokitellaan hylkääviin ja hyväksyviin, joita molempia voi automaatissa olla useita. Esimerkin -tila on hyväksyvä, koska tilaa esittävällä ympyrällä on kaksinkertainen reunus. Tila on hylkäävä, koska sitä esittävällä ympyrällä on yksinkertainen reunus. utomaatin ajatellaan vastaavan myöntävästi, jos se on koko syötteen luettuaan hyväksyvässä tilassa. Se mistä tilasta aloitetaan, merkitään tyhjästä tulevalla nuolella. Yllä olevassa esimerkissä aloitustila on. nnetaan esimerkin automaatille syöte. Ensimmäisenä syötteessä siis annetaan ja se ei aiheuta siirtymää tilasta. Seuraavaksi syötteessä annetaan l ja se aiheuttaa siirtymän tilaan. Viimeisenä syötteessä annetaan toinen ja se aiheuttaa siirtymän tilasta takaisin tilaan, joka on hyväksyvä. Syöte siis hyväksytään. Jos syötteenä on, niin se hylätään. Kolme ensimmäistä bittiä ovat samoja kuin yllä ja niiden jälkeen ollaan tilassa. Viimeinen siirtää automaatin tilasta tilaan. Esitetty automaatti siis hyväksyy bittijonot, joissa on parillinen määrä -bittejä. utomaatissa voi tietenkin olla useampia tiloja ja myös hyväksyviä/hylkääviä tiloja voi olla useita. Nimensä mukaisesti automaatin tilojen määrä on kuitenkin oltava äärellinen. Kysymykset ovat seuraavalla sivulla.
Kysymys 2.a) Mitkä seuraavista nelibittisistä bittijonoista alla oleva automaatti hyväksyy ja mitkä se hylkää. Tutkittavat bittijonot ovat,, ja. (4 pistettä) D Vastaus: : : : : D Hyväksytty bittijono : : D : D : D Hyväksytty bittijono : : D : D : Hylätty bittijono : : D : D : D Hyväksytty bittijono Kysymys.b) nna kaksi esimerkkiä bittijonoista, jotka alla oleva automaatti hyväksyy ja kaksi esimerkkiä bittijonoista, joita alla oleva automaatti ei hyväksy. (4 pistettä)
Vastaus: Hyväksyy : : : : Hyväksyy : : : Hylkää : : : Hylkää : : : Itse asiassa automaatti hyväksyy jonoja, joissa on 3:lla jaollinen määrä :ia. Kysymys.c) Tee automaatti, joka hyväksyy bittijonot, joiden kaksi viimeistä bittiä ovat - bittejä. (5 pistettä) Vastaus: Tähän olisi lisäksi hyvä kirjoittaa em. tavalla testi siitä, miten eri jonot käyttäytyvät (ht). Kysymys.d) Tee automaatti, joka hyväksyy kaikki sellaiset bittijonot, joissa ei esiinny bittijonoa. (6 pistettä)
Vastaus: D, Tähän olisi lisäksi hyvä kirjoittaa em. tavalla testi siitä, miten eri jonot käyttäytyvät (ht). Kysymys 2.e) Tee automaatti, joka hyväksyy bittijonot, joissa esiintyy molemmat bittijonot, sekä, että. (6 pistettä) Vastaus: D E F G H, Tähän olisi lisäksi hyvä kirjoittaa em. tavalla testi siitä, miten eri jonot käyttäytyvät (ht). Tehtävä 2. a) Tee aliohjelma, joka hyväksyy samat jonot kuin tehtävän b) automaatti. (24 p) b) Mitä kieliä liitteessä ( p) (vastaus oli Java ja Python). Seuraavaksi erilaisia mallivastauksia Javalla. # ratkaisut olisivat hyvin samanlaisia. Erona lähinnä että yhden kirjaimen jonosta voi ottaa int arvo = jono[i]-''; Esimerkeissä syöte on otettu nimenomaan merkkijonona, jolloin on helpompi kutsua erilaisilla arvoilla. Tehtävää voi lähestyä monella eri tavalla. Kukaan vastaajista ei ollut tehnyt yleistä automaattia vastaavaa ohjelmaa, joka olisi ollut yksi mahdollisuus. Lyhyin ratkaisu Javalla olisi käyttää säännöllisiä lausekkeita: return Pattern.matches("((*){3)**",jono);
Seuraavaksi lyhyin on ehkä todeta, että automaatti laskee onko jonossa 3:lla jaollinen määrä nollia: int lkm = ; for (int i=; i<jono.length(); i++) if ( jono.chart(i) == '' ) lkm++; return lkm % 3 == ; Sitten voisi tehdä automaattia matkivan if-lause-hässäkän: int tila = ; for (int i=; i<jono.length(); i++ ) { int arvo = jono.chart(i)-''; if ( tila == && arvo == ) tila = ; else if ( tila == && arvo == ) tila = ; else if ( tila == && arvo == ) tila = 2; else if ( tila == && arvo == ) tila = ; else if ( tila == 2 && arvo == ) tila = ; else if ( tila == 2 && arvo == ) tila = 2; return tila == ; Tästä toki voisi jättää pois siirtymät, jotka eivät muuta tilaa: int tila = ; for (int i=; i<jono.length(); i++ ) { int arvo = jono.chart(i)-''; if ( tila == && arvo == ) tila = ; else if ( tila == && arvo == ) tila = 2; else if ( tila == 2 && arvo == ) tila = ; return tila == ; Tätä voi vielä sieventää havainnolla, että siirtymiä tapahtuu vain arvon ollessa, eli: int tila = ; for (int i=; i<jono.length(); i++ ) { int arvo = jono.chart(i)-''; if ( arvo == ) tila++; if ( tila >= 3 ) tila = ; return tila == ; Kaikkien em. automaattien muuttaminen tekemään jotakin muuta, on työlästä ja riskialtista. Sitten jos haluaa oloita käyttämättä tehdä enemmän muunneltavan automaatin, voisi sen tehdä taulukon avulla. Taulukossa annetaan kunkin solmun siirtymät sekä tieto siitä, onko solmu hyväksyvä vaiko ei:
int[][] kolmellajaollinenmaaranollia = { // hyv {, '', '', // {, '', '', // {, '', '', // ; utomaatin kutsu olisi nyt: ja itse aliohjelma: boolean hyvaksyy = tutki(kolmellajaollinenmaaranollia,jono); public static boolean tutki(int[][] siirtymat, String jono) { int tila = ; for (int i=; i<jono.length(); i++ ) { int arvo = jono.chart(i)-''; // '' ->, '' -> if ( arvo < < arvo ) return false; if ( siirtymat[tila][] == tila && siirtymat[tila][2] == tila ) break; // tila josta ei enää voida siirtyä muualle tila = siirtymat[tila][arvo+] - ''; // '' ->, '' -> jne.. if ( tila < siirtymat.length <= tila ) return false; return siirtymat[tila][] == ; Tästä olisi helppo tehdä muiden ensimmäisen tehtävän automaatit muuttamalla vain taulukkoa. Miten?
Mallivastauksia muilla kielillä: Esimerkin vuoksi vielä mallivastaus kahdella hieman erilaisella kielellä. Haskell: -- Määritellään mitkä ovat mahdolliset tilat ja syötteet data State = deriving (Eq,Show) data Input = O I deriving (Eq,Show) -- Määritellään mihin tilaan siirrytään annetusta lähtötilasta annetulla syötteellä step :: State -> Input -> State step O = step I = step O = step I = step O = step I = -- Syötetään listallinen syötteitä automaatille ja palautetaan tila, johon se päätyi. run :: [Input] -> State run = foldl step -- jetaan esim. ghci-ohjelmalla komennolla: run [O, I, O, I, O, I] Lisp: Tämä Lisp-versio toimii sekä Emacs Lispissä, että ommon Lispissä (esim. sbcl). (defvar *tk22b* '((a (b a) t) (b (c b) nil) (c (a c) nil)) "Tilakone, joka esitetään listana tiloja. Jokainen tila esitetään listana, jonka alkiot ovat - tilan nimi, - tilan siirrokset ja - onko tila kelvollinen lopputila. Esimerkiksi (a (b a) t), tarkoittaa, että tilan nimi on a, tilan siirrokset ovat arvolla tilaan b sekä arvolla tilaan a, ja tila on kelvollinen lopputila (t - tosi, nil - epätosi).") (defun get-state-transition (state tr) "Palauttaa seuraavan tilan nykyisen tilan ja siirroksen perusteella." (nth tr (second state))) (defun is-state-final (state-name) "Testaa, onko tila lopputila vai ei. Palauttaa t, jos tila on lopputila, muuten nil." (third (get-state-by-name state-name))) (defun get-state-by-name (state-name) "Palauttaa tilan nimen mukaisen tilan." (assoc state-name *tk22b*))
(defun tk2-next (state-name move) "Tilakoneen siirrosfunktio, joka ottaa syötteenään nykyisen tilan nimen ja tilamuutoksen ( tai ), ja palauttaa arvonaan seuraavan tilan." (get-state-transition (get-state-by-name state-name) move)) (defun tki2 (state current rest) "Tilakone rekursiivisena funktiona. Ottaa syötteenä alkutilan, syötön ensimmäisen alkion ja syötön loput alkiot. Palauttaa t jos tilakone on syötteen loputtua hyväksyvässä tilassa, muuten nil." (if (eq current nil) (is-state-final state) (tki2 (tk2-next state current) (first rest) (rest rest)))) (defun tk2 (input) "Tilakoneen käynnistävä funktio. Ottaa syötteenään listan ykkösiä ja nollia. Palauttaa t jos tilakone on syötteen loputtua hyväksyvässä tilassa, muuten nil." (unless (null input) (tki2 'a (first input) (rest input)))) ;;; Testataan vähän. ssertin sijaan olisi parempi käyttää ;;; jotain tätä varten luotua testaustyökalua ;;; (esim. Emacs Lispillä ert). (assert (not (tk2 '()))) (assert (not (tk2 '()))) (assert (tk2 '())) (assert (tk2 '( ))) (assert (tk2 '( ))) (assert (tk2 '( ))) (assert (tk2 '( ))) (assert (tk2 '( ))) (assert (tk2 '( ))) (assert (not (tk2 '( )))) ;;; Esimerkki testistä Emacsissa Ert:llä: ;;; (ert-deftest ert-test-tk2 () ;;; (should (tk2 '( ))) ;;; (should-not (tk2 '( ))))