Arvutiteaduse instituut
  1. Esileht
  2. Automaadid, keeled ja translaatorid
EN
Logi sisse

Automaadid, keeled ja translaatorid

  • Üldinfo
  • Ajakava
  • Eksami näidised
  • Teemad
    • 1. Soojendus
    • 2. Regulaaravaldised
    • 3. Automaadid
      • JFLAP
      • Programmeerimine*
        • Mini AKTK masinad
        • FormatMachine
        • Mealy masinad
      • NFA ehitusklotsid
      • Püsipunktid*
      • Kodutöö
    • 4. Avaldise struktuur
    • 5. Grammatikad ja lekser
    • 6. Käsitsi parsimine
    • 7. ANTLRiga töötamine
    • 8. Interpretaator
    • 9. Kompilaator
    • 10. Edasi!
  • Süvendus
  • Bitbucket
  • Moodle
  • Zulip
  • Zoom

Olekutega programmeerimine

Teemat tutvustav video: Olekumasinatega programmeerimine.

Teoorias huvitavad meid peamiselt olekumasinad, mis vastavad jah/ei küsimusele: kas sõne kuulub regulaaravaldise poolt defineeritud hulka. Praktikas kasutatakse olekumasinaid, mis lisaks veel midagi teevad, aga käituvad erinevalt sõltuvalt oma seisundist. Üks standard selliste praktilisemate olekumasinate joonistamiseks on UML State Machine. Alustuseks on siin üks lihtne klassikaline näide olekupõhisest programmist. Edasi võib vaadata järgmisi materjale:

  • Esimese kodutöö saab väga ilusasti olekumasinatega lahendada.
  • Olekupõhist mõtlemisvõimet võib proovile panna teksti puhastamise ülesandega, FormatMachine.
  • Kui olekumasinad väga meeldivad, siis on meil üks tore lisaülesanne, et seda kõike teha formaalse Mealy masinaga.

HtmlStrip

Meie eesmärk on kirjutada meetod, mis eemaldab kujunduskäske (tags) HTML dokumendist. Proovime kõigepealt siis kirjutada sellise meetodi, mis kõige lihtsama sisendi puhul töötab:

  • Sisend: <b>foo</b>
  • Eeldatav väljund: foo

Meetodi signatuur võiks olla järgmine:

public class HtmlStrip {
    public static String removeHtmlMarkup(String s) {
        StringBuilder sb = new StringBuilder();
        // TODO: midagi peaks ilmselt tegema.
        return sb.toString();
    }
}

Kirjutame kohe ka esimese testi:

public class HtmlStripTest {
    @Test
    public void testRemoveHtmlMarkup() throws Exception {
        check("foo", "<b>foo</b>");
    }

    public void check(String expected, String input) {
        assertEquals(input, expected, HtmlStrip.removeHtmlMarkup(input));
    }
}

Alustame lihtsa ideega

Idee võiks olla selline, et loeme tähthaaval sisendit ja me lihtsalt kopeerime seda väljundisse (echo). Kui aga näeme märki <, siis lähme ignoreerimise režiimi. Seda ideed võib joonistada olekumasinana, mille põhjal saame järgmist koodi:

    public static String removeHtmlMarkup(String s) {
        StringBuilder sb = new StringBuilder();
        boolean tag = false;
        for (char c : s.toCharArray()) {
            if (c == '<') {
                tag = true;
            } else if (c == '>') {
                tag = false;
            } else if (!tag) {
                sb.append(c);
            }
        }
        return sb.toString();
    }

NB! Implementatsioon on pisut vigane, nimelt sisendi <b>f>5</b> puhul peaks väljund olema f>5, aga programm annab teise vastuse. Olekumasin (paremal) on siiski korrektne, seega ei tohiks liiga raske olla seda viga parandada. Palun tee seda! Nii, väga hea, aga meil on kahjuks suurem mure: <a href='>'>foo</a>. Selliste sisenditega me ei olnud arvestanud!

Natuke keerulisem sisend

Meil oleks uut olekut vaja, et vältida seda probleemi, kui sõnede sees esineb '>'. Kõigepealt lisame vajalike teste, sest meile ei meeldi pimedas programmeerida:

    @Test
    public void testRemoveHtmlMarkupWithQuotes() throws Exception {
        check("foo", "<a href='>'>foo</a>");
    }
    @Test
    public void testRemoveHtmlMarkupWithJustQuotes() throws Exception {
        check("'foo'", "'<b>foo</b>'");
        check("'foo'", "'foo'");
    } 

Nüüd joonistame uue automaadi ja kirjutame ka sellele vastav kood:

    public static String removeHtmlMarkup(String s) {
        StringBuilder sb = new StringBuilder();
        boolean tag = false;
        boolean qte = false;
        for (char c : s.toCharArray()) {
            if (c == '<' && !qte) {
                tag = true;
            } else if (c == '>' && tag && !qte) {
                tag = false;
            } else if (c == '\'' && tag) {
                qte = !qte;
            } else if (!tag) {
                sb.append(c);
            }
        }
        return sb.toString();
    }

Ma ei ole veel päris kindel...

See kood arenes üsnagi katse-eksitus meetodil. Ma ei tea, mis ta täpselt teeb, sest need kõrvaltingimused on päris keerulised. Me peame sõltuvalt sisendist ja automaadi olekust mingi otsuse tegema. Siiamaani oleme põhiliselt sisendi järgi hargnenud ja siis oleku peale mõelnud. Kirjutame nüüd aga rohkem olekumasina vaatevinklist:

    public static String removeHtmlMarkup(String s) {
        final int INI = 0;
        final int TAG = 1;
        final int QTE = 2;
        StringBuilder sb = new StringBuilder();
        int state = INI;
        for (char c : s.toCharArray()) {
            switch (state) {
                case INI:
                    if (c == '<') state = TAG;
                    else sb.append(c);
                    break;
                case TAG:
                    if (c == '>') state = INI;
                    if (c == '\'') state = QTE;
                    break;
                case QTE:
                    if (c == '\'') state = TAG;
                    break;
            }
        }
        return sb.toString();
    }

Selle implementatsiooni suur eelis on see, et on palju kergem veenduda, et kood vastab mudelile. Eelmine lahendus läbib ka minu teste, aga kes teab... Viimase koodi suhtes olen palju optimistlikum! Samas, alati ei pruugi oleku järgi hargnemine olla kõige mugavam. Järgnev ülesanne on näiteks selline, kus minu jaoks oli mugavam sisendi järgi ikkagi hargneda. Alati on aga abiks olekumasina välja joonistamine paberil!

Harjutusmasinate liides

Kirjutame siin ülaloleva koodi ümber formaadis, mis on rohkem olekumasina moodi. See on meil tegelikult ainult selleks oluline, et kui lahendad iseseisvalt meie harjutusi sellel teemal, siis see julgustab Sind tõesti tähthaaval sõne läbida. Näitame siin ka sellise lahenduse, kus hargneme põhiliselt tähtede järgi (nii on mõnikord mugavam), aga ikkagi on kasutatud paberil joonistatud automaati, et paremini aru saada toimuvast. Olekud on seetõttu mõistlikumad: nad ei ole eraldi muutujates hajutatud. Tuleb implementeerida klass HtmlStripMachine, millel on meetod process ja raamistik hoolitseb selle eest, et tähthaaval sisendit sellele sööta:

    private static String cleanUp(String s) {
        StringBuilder sb = new StringBuilder();
        HtmlStripMachine machine = new HtmlStripMachine();
        for (char c : s.toCharArray()) sb.append(machine.process(c));
        return sb.toString();
    }

Kui olekumasina joonist vaadata, siis on selge, et väljastamine toimub ainult algseisundis, kui sisendtäht ei ole <, seetõttu võime kõigepealt otsustada, kas on vaja midagi väljastada või mitte. Me peame seda kohe algul otsustama, kuna seisund võib pärast muutuda. Salvestame otsuse tulemust muutujas echo. Ülejäänud loogika on üsna otse joonise pealt tulnud:

import static week3.machines.HtmlStripMachine.State.*;

public class HtmlStripMachine {
    enum State {
        INI, TAG, QTE
    }
    private States state = INI;

    public String process(char c) {
        boolean echo = state == INI;
        switch (c) {
            case '<':
                if (state == INI) state = TAG;
                return ""; // Seda ei väljastata!
            case '>':
                if (state == TAG) state = INI;
                break;
            case '\'':
                if (state == QTE) state = TAG;
                else if (state == TAG) state = QTE;
                break;
        }
        return echo ? Character.toString(c) : "";
    }
}

Sinu kord

Võiks proovida, et ka tavaliste jutumärkidega töötaks. Siis peab olema nii, et kui alustame ühte tüüpi jutumärkidega, siis lõpetame ainult sama tüüpi jutumärgiga: "John's" ja 'Ta ütles, "Tere!"'. Vajalikud testid on failis HtmlStripMachineTest.java.

  • 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.
Tartu Ülikooli arvutiteaduse instituudi kursuste läbiviimist toetavad järgmised programmid:
euroopa sotsiaalfondi logo