Arvutiteaduse instituut
  1. Kursused
  2. 2019/20 kevad
  3. Automaadid, keeled ja translaatorid (LTAT.03.006)
EN
Logi sisse

Automaadid, keeled ja translaatorid 2019/20 kevad

  • Üldinfo
  • Eksami näidised
  • Kava
    • 1. Soojendus
    • 2. Regulaaravaldised
    • 3. Olekumasinad
    • 4. Avaldise struktuur
      • Avaldispuu läbimine
      • Eksami alusosa!
      • Pythoni avaldiste struktuur*
      • Java AST analüüs*
      • Regulaaravaldiste analüüs
      • Raskemad harjutused*
      • Halba teha ei täi*
      • Kodutöö
    • 5. Grammatikad ja lekser
    • 6. Käsitsi parsimine
    • 7. ANTLRiga töötamine
    • 8. Interpretaator
    • 9. Kompilaator
  • Bitbucket
  • Moodle
  • Fleep!

Isabelle: Kui halba teha ei täi

Tihti väidame siin kursusel, et tahame kirjutada sellist usaldusväärsemat koodi, aga see jääb natuke intuitsiooni tasemel. Kindlasti on puhas programmi disain väga oluline samm headuse poole, sest halba tegemise tõenäosus on oluliselt vähem. Kuidas saaks aga olla kindel, et programm ikkagi midagi halba ei tee?

Üks võimalus on kasutada selline keskkond, kus saame programmi kohta väiteid tõestada ja arvuti kontrollib, kas ta on nende väidetega nõus. Selles ülesandes tutvume süsteemiga Isabelle. Kõigepealt tuleb seda siis endale paigaldada. Tegemist on iseseisva Java rakendusega, mille paigaldamine peaks üsna lihtne olema. Esimesel käivitamisel ehitab ta valmis oma standardteeki, mis võtab üsna palju aega!

Eeldused

Selle ülesanne lahendamiseks pead olema valmis endale funktsionaalprogrammeerimise mõned põhitõdesid väga põgusa sissejuhatuse põhjal endale selgeks tegema: valguse tee ei ole lihtne tee.

Teiseks on vaja kõigepealt lahendada ExprTransformer.java ülesandeid. Kui meetod elimNeg on Javas lahendatud, siis saame nüüd keskenduda sellele, et programmi kohta väiteid tõestada. Meil on siis aritmeetiline avaldis, kus esineb ainult liitmine ja unaarne miinus. Tahame lõpuks teisendada seda selliseks, kus kõik miinusmärgid on ainult lehtedes.

Programmi mall

Kõigepealt tuleks siis luua endale uus fail nimega akt.thy ja selle sisse kirjutada meie avaldise andmestruktuuri:

theory akt
imports Main
begin

datatype exp = Num int | Add exp exp | Neg exp

definition ex1 :: exp where 
"ex1 = Neg (Add (Num 5) (Add (Neg (Num 3)) (Num (-4))))"

end

NB! Siin peab ka mooduli nimi (theory akt) langema kokku faili nimega. Me defineerime siis oma exp tüüpi andmestruktuuri meie tuttavate variantidega. Avaldis on kas arvkonstant (Num), kahest avaldisest koosnes liitmine (Add) või avaldise vastand (Neg). Need konstruktorid saab kasutada peaaegu nagu Java. Kasutame aga matemaatilist funktsiooni rakendamise notatsiooni, nagu näiteks cos (x+1) + sin y + 1.

Avaldise väärtustamine

Defineerime nüüd meetod eval. Siin tuleb lihtsalt täita lüngad (???). Me defineerime funktsioone kasutades loogikavõrdus, mida pannakse jutumärkidesse. See on väga veider asi Isabelle süntaksi juures, millega tuleb praegu lihtsalt leppida, et nii on...

fun eval :: "exp ⇒ int" where
"eval (Num n) = ??? " |
"eval (Add e1 e2) = eval e1 ??? eval e2" |
"eval (Neg e) = ??? (eval e)"

value "eval ex1"

Kui oled definitsiooni korda saanud, siis saame avaldise väärtust küsida selle value käsuga. Selleks peab ka kursus viia sellele reale ja all output aknas peaks ilmuma ka tulemus.

Lehtedes esinevad arvud

Defineerime nüüd meetod, mis oli Transformer.java klassis nimega valueList, aga siin on tal lühem nimi, sest IDE automaatselt ei kirjuta nimed lõpuni...

fun vals :: "exp ⇒ int list" where
"vals (Num n) = [n]" |
"vals (Add e1 e2) = vals e1 @ vals e2" |
"vals (Neg e) = vals e"

Selles näites kasutame Isabelle'i liste: jälgi singleton ja listi ühendamise süntaksit. Võid jälle proovida arvutada value "vals ex1", aga nüüd on Sinu kord implementeerida funktsiooni, mis oli Java harjutuses nimega signedValueList. See peaks tagastama väärtuste listi, kus on arvud "õigete" märkidega. Lihtsam on seda defineerida abifunktsiooni kaudu, mis võtab märk endale argumendina:

fun sigvals' :: "exp ⇒ int ⇒ int list" where
"sigvals' (Num n) sign = ???" |
"sigvals' (Add e1 e2) sign = ???" |
"sigvals' (Neg e) sign = ???"

fun sigvals :: "exp ⇒ int list" where
"sigvals e = sigvals' e 1"

Nüüd tõestamine!

Kui me arvutame selle sigvals listi summa (kasutades Isabelle standardteeki funktsiooni sum_list), siis peaksime saama avaldise väärtust tagasi. Proovi kõigepealt value "sum_list (sigvals ex1)", aga üldiselt peaks siis kehtima järgmine teoreem, mida üritame kohe induktsiooniga tõestada!

theorem "sum_list (sigvals e) = eval e"
apply (induction e)
apply auto
done

Nüüd tuleb liikuda sinna, kus Isabelle on pahandanud ja vaadata output (või paremal state) vaadet. Seal peaks olema kirjas, et kinni on ta jäänud sellise asja tõestmisega:

sum_list (sigvals' e 1) = eval e ⟹ sum_list (sigvals' e (-1)) = - eval e

See on tegelikult üsna sisuline koht, kus Isabelle on kinni jäänud. Tegelikult oleksime pidanud midagi taolist induktsiooniga tõestama ja siis saame seda oma põhitulemuses kasutada:

lemma sigval_sum: "sum_list (sigvals' e (-1)) = - eval e"
apply (induction e)
apply auto
done

theorem "sum_list (sigvals e) = eval e"
apply (auto simp: sigval_sum)
done

See ka ei tööta, sest meil on ikkagi paralleelselt vaja induktsiooniga tõestada võrdused nii positiivse kui ka negatiivse argumendi korral. Pane kokku õige sigval_sum lemma, et teoreem läbi läheks. Konjunktsiooni kirjutamiseks võib toksida järjest /\ ja seda teisendatakse õigeks märgiks. Lahenduse vaatamiseks vajuta siia:

sum_list (sigvals' e 1) = eval e ∧ sum_list (sigvals' e (-1)) = - eval e

Kui oled sellega hakkama saanud, siis proovi oma koodis sisse viia mõned vead, näiteks "unustame" mõnes definitsioonis Neg harus miinusmärki. Kas Isabelle saab aru, et kood on vigane?

Ülesandeid iseseisvaks lahendamiseks

Edasi on siis kaks ülesannet, mida võiks analoogia põhjal saada iseseisvalt lahendada. Need on lihtsad tõestused ja rohkem Isabelli süntaks ei tohiks vaja minna kui siin lehel kirjas. Kuna aga kasutame siin automatiseeritud tõestamistaktikad, siis võib asi raskeks minna, kui kasutad (samaväärseid) aga erineva kujuga valemid kui need, millega mina katsetasin. Seega on parem abi küsida, kui asi hakkab minema väga keeruliseks.

1. Sigval korrektsus

Isabelle kontrollib ainult, et meie tõestus on korrektne, aga kas teoreem ise on ka õige asi? Ta ei ole nüüd otseselt vale, aga nagu ka järjestamisel on see, et kui nõuad ainult, et järjestus algoritmi tulemus oleks järjestatud list, sisi võib tagastada tühja listi ka. Teine nõua järjestamise puhul on see, et tulemus peaks sisaldama kõik sisendis ette antud elemente! Meil on siin ka samamoodi. Jääb tõestada: List.map abs (vals x) = List.map abs (sigvals x).

2. Eituste elimineerimine

Meil oli Java ülesandes ka meetod elimNeg. Selle jaoks on kõik üsna analoogiline nagu meil sigval puhul. Defineeri järgmine funktsioon ja tõesta tema kohta vastav teoreem!

fun elim_neg :: "exp ⇒ exp" 
theorem "vals (elim_neg x) = sigvals x"

Esitamine

Koodi tuleb esitada moodle'is, aga lisaks tuleb oma lahendust ka Vesalile loengu või praktikumi järel selgitada ja oma kogemustest rääkida.

  • Arvutiteaduse instituut
  • Loodus- ja täppisteaduste valdkond
  • Tartu Ülikool
Tehniliste probleemide või küsimuste korral kirjuta:

Kursuse sisu ja korralduslike küsimustega pöörduge kursuse korraldajate poole.
Õppematerjalide varalised autoriõigused kuuluvad Tartu Ülikoolile. Õppematerjalide kasutamine on lubatud autoriõiguse seaduses ettenähtud teose vaba kasutamise eesmärkidel ja tingimustel. Õppematerjalide kasutamisel on kasutaja kohustatud viitama õppematerjalide autorile.
Õppematerjalide kasutamine muudel eesmärkidel on lubatud ainult Tartu Ülikooli eelneval kirjalikul nõusolekul.
Courses’i keskkonna kasutustingimused