Finita automater, reguljära uttryck och prefixträd
Algoritmer och Datastrukturer Markus Saers
markus.saers@lingfil.uu.se
Upplägg
Finita automater Implementation Reguljära uttryck
Användningar i Java Prefixträd
Alternativ till binära sökträd för strängar Kallas ofta trie
Finita automater
Består av:
en mängd tillstånd (K) en mängd symboler (Σ) ett starttillstånd (q0)
en mängd giltiga avslutningstillstånd (F) en mängd övergångar (δ)
Funktion: K × Σ → K
K = {1, 2, 3}
Σ = {a, b}
q0= 1
δ = {<1, a>=2, <2, a>=2, <2, b>=3}
F = {3}
Finita automater
1 a 2 3
a b
Olika finita automater
Indeterministiska Tillåter epsilonövergångar Tillåter att δ är en relation
Flera måltillstånd för varje tillstånd–symbol-par Kan ”determiniseras” maskinellt Deterministiska
Tillåter inga epsilonövergångar Tvingar δ att vara en funktion I alla praktiska hänseenden kan man betrakta finita automater som deterministiska
Finita automater
Beskriver reguljära språk
Varje finit automat motsvarar ett reguljärt språk Om man driver en automat med en sträng hamnar man i ett giltigt avslutningstillstånd om strängen ingår i det reguljära språk automaten beskriver
Man kan betrakta strängar som en slags drivmedel för automater
Sträng = Σ*
Provkörning med ”aab”
1 a 2 3
a b
a a b
Provkörning med ”aab”
1 a 2 3
a b
a b
Provkörning med ”aab”
1 a 2 3
a b
b
Provkörning med ”aab”
1 a 2 3
a b
Total funktion som δ
δ bör vara en total funktion
Potentiella övergångar som utelämnas behandlas som om de gick till ett återvändstillstånd
1 a 2 3
a b
x
b b
b a a
Implementationer av finita automater
Hur ska man gå till väga för att modellera en finit automat?
Modellera delarna för sig K: Alla tillstånd
Σ: Alla symboler q0: Starttillståndet
F: Alla giltiga avslutningstillstånd δ: Övergångsfunktionen
K och q
0Alla tillstånd måste vara unika Vanligtvis numrerade Kan modelleras som heltal
Varje heltal motsvarar ett tillstånd Vi kan modellera K som antalet tillstånd q0är i så fall ett specifikt heltal som pekar ut starttillståndet
Σ
En mängd unika symboler
Kan modelleras som en lista med tecken Teckenuppsättningen är en lista med tecken!
Kan bli jobbigt med UTF-8 Läsa byteström istället?
Varje tecken tolkas som en egen automat, somliga med ett tillstånd, andra med flera
Begränsar teckentabellen till 255 unika symboler
Kan modelleras genom en separat klass Måste kunna ge ett heltal givet en symbol
F
Eftersom tillstånden modelleras som heltal behöver en boolean associeras till varje heltal
Svarar på frågan ”är detta ett giltigt avslutningstillstånd?”
En array med booleaner passar
δ
En funktion ska leverera ett svar för varje tänkbart input
Tänkbara input är kombinationer av tillstånd och symboler (K×Σ) Tillstånd och symboler är heltal Vi behöver associera två heltal (starttillståndet och en symbol) till ett tredje (måltillståndet)
Array med arrayer med heltal!
Matris med heltal
Sammanfattning
K: Heltal (antal tillstånd) Σ: Association: symbol heltal
Datorns egna teckentabell Måste kunna svara på antalet tecken q0: Heltal (som pekar ut starttillståndet) F: Array (storlek = K) med booleaner δ: Matris (storlek = K × size(Σ)) med heltal
I Java
public class FSA { private int k;
private int q0;
private boolean[] f;
private int[][] delta;
public FSA(int k, int q0) { this.k = k;
this.q0 = q0;
f = new boolean[k];
delta = new int[k][256];
}
public boolean accepts(String s) { int q = q0;
for (byte b : s.getBytes()) { b -= Byte.MIN_VALUE; //Skalning q = delta[q][b];
}
return f[q];
}
Exempelautomaten i Java
k = 4 q0 = 1
f = [ false, false, false, true ] delta = [ […, 0, 0, …],
[…, 2, 0, …], […, 2, 3, …], […, 0, 0, …] ]
1 a 2 3
a b
0
b b
b a a
Sammanfattning Finita automater
Beskriver reguljära språk…
Genom att svara på frågan om en given sträng ingår i språket eller inte
Kan implementeras relativt enkelt
Reguljära uttryck
Beskriver ett reguljärt språk Kan kombineras med
Kleene stjärna (*) Union (U) Snitt (∩) Konkatenering (_) Exempel
A = {a}, B = {b}
A_B = {ab}
AB U BA = {ab, ba}
A*B = {b, ab, aab, aaab, … }
Reguljära uttryck
Reguljära språk kan vara oändliga Vi kan inte räkna upp alla strängar som
ingår i ett språk
Kan vi avgöra ifall en given sträng tillhör ett givet reguljärt språk?
Om vi hade en automat…
Reguljära uttryck och finita automater
Båda beskriver reguljära språk Går att översätta maskinellt mellan reguljära uttryck och finita automater Två aspekter av samma sak
Reguljära språk
Reguljära uttryck i Java (1.4 +)
java.util.regex Pattern
Klass som representerar reguljära uttryck Byggs med en strängrepresentation av det
reguljära uttrycket
Dokumentationssidan innehåller allt man kan tänkas vilja veta om Javas reguljära uttryck Matcher
Byggs med ett Pattern och en String Svara på frågor om hur strängen matchar det
reguljära uttrycket
Reguljära uttryck i String
Vanligaste användningsområdena finns inbäddade i String
boolean matches(regex)
String replaceAll(regex, replacement) String replaceFirst(regex, replacement) String[] split(regex)
String[] split(regex, limit)
Reguljära uttryck i programmering
Förkortas ofta re (regular expression)
perlre = Perls manualsida för reguljära uttryck regex (regular expression)
Java.util.regex
regexp (regular expression) re(gex(p)?)? ☺
Ofta utbyggda för att ta tillvara på saker man får gratis vid implementering Stöd för datorns inbyggda teckentabell
Reguljära uttryck i programmering
Bonusfunktioner Färdiga symbolklasser
Anpassning till datorns teckentabell Många tecken är redan klassificerade Exakt vad i strängen var det som matchade uttrycket?
Möjlighet att få ut delsträngar som matchade delar av uttrycket
Reguljära uttryck i Java
Tecken Teckenklasser
[a-zåäö] Alla tecken mellan a och z samt åäö (intervall enligt gällande teckentabell) [^…] Negerad klass
Många färdiga klasser Kvantifierare
Hur många av föregående tecken, klass eller grupp?
Grupperingar Görs med parenteser
Teckenklasser
(se java.util.regex.Pattern)
Mängdteoretiska operationer [a-z&&[def]] = [def]
[a-z&&[^def]] = [a-cg-z]
Escapetecken
\\ backslash
\n \t \r \f Vanliga system betydelse
\Onn Tecken nn enligt oktal värde
\xnn Tecken nn enligt hexadecimalt värde Fördefinierade klasser
. Matchar vilket tecken som helst
\s \S ”space”
\w \W ”word”
\d \D ”digit”
\p{namn} \P{namn} ”posix”
Ankare
^ Början av input
Kvantifierare
Grundkvantifierare
? Noll eller en gång
* Noll eller fler gånger + En eller fler gånger {n} Exakt n gånger
{n,} Minst n gånger
{n,m} Minst n gånger, högst m gånger Är normalt ”giriga”
Kan göras ”ovilliga” genom att lägga på ett ”?” efter (??, *?, +?, …)
Giriga/ovilliga kvantifierare (greedy/reluctant)
Viktigt koncept när man fångar olika grupper
Giriga kvantifierare försöker matcha så mycket som möjligt
Ovilliga kvantifierare försöker matcha så lite som möjligt
”12, 220, 34, 4” ska matchas mot ”.*,”
Girig matchning: ”12, 220, 34,”
Ovillig matchning: ”12,”
Grupperingar
Hela uttrycket betraktas som en grupp Grupper kan skapas genom parenteser Grupper fångar upp den delsträng de matchar
Går att stänga av: (?:RegExp) Exempel: ”(A)(B(C))”
Hela uttrycket blir grupp 0
Därefter tas varje startparentes från vänster till höger ut som en grupp: A, BC, C
Matchande delsträngar i Java
Klassen Matcher kan hämta ut en grupp efter index
Exempel: ”(A)(B(C))”
groupCount() = 4 group(0) = ABC group(1) = A group(2) = BC group(3) = C
Förhållandet mellan Pattern, Matcher och String
Pattern har en statisk metod som heter compile Tar en strängrepresentation av ett reguljärt uttryck och
returnerar en Pattern
Pattern har en metod som heter match Tar en CharSequence (supertyp till String) som
argument och returnerar en Matcher Matcher representerar resultatet av en matchning
Har metoder för att beskriva hur det gick (se grupper)
Förhållandet mellan Pattern, Matcher och String
Pattern
Matcher compile
Pattern match String
group(int) String String
Sammanfattning:
reguljära uttryck
Enkelt att behandla strängar Kraftfull mekanism
Tidskomplexitet: O(n) där n är strängens längd
Prefixträd (trie)
Arbetar på sekvenser ex. strängar Jag kommer genomgående tala om strängar
istället för ”sekvenser av tecken”
Har mycket gemensamt med automater Informationen om vad man läst in hittills i en
automat avspeglas i vilket tillstånd man befinner sig i
I prefixträd motsvarar varje tillstånd (eller nod om man så vill) ett unikt prefix Noderna kan associeras med information om det aktuella prefixet
Prefixträd egenskaper
Vi kan associera information med en sträng
Vi kan komma åt informationen i O(n) tid där n är längden på strängen
Jättesnabbt!
Lexikon?
Exempel
i s
t
e r
b
j ö r n
NN
NN NN
NN
Insättning av ”brunbjörn”:
Automatsättet
i s
t
e r
b
j ö r n
brun b
NN NN NN ?
Insättning av ”brunbjörn”:
Prefixträd
i s
t
e r
b
j ö r n
brun
j ö r n
b
NN
NN NN
NN
NN
Skillnader mellan
automater och prefixträd
Automater Går att minimera
Kan svara på ifall strängen tillhör språket eller inte
Prefixträd
Går inte att minimera – spretigare och större Vet alltid vilken sträng som krävdes för att
hamna där man befinner sig
Prefixträd eller binära sökträd?
s = stränglängd, n = antal noder Binära sökträd
O(log n)
Behöver göra log n strängjämförelser Hur lång tid tar en strängjämförelse?
O(s)
Prefixträd O(s)
Behöver göra s symbolassociationer Hur lång tid tar en symbolassociation Beror på vilken mekanism som används
Prefixträd eller binära sökträd?
Symbolassociation
Omvandla symbolen till ett nummer Slå upp i array
Snabbt Tar stor plats Slå upp med hashning
Långsammare Tar mindre plats
Prioriteringsfråga
Svårt att säga vad som är snabbast Beror till stor del på konstanter som
inte syns i O-notation
Prefixträd eller binära sökträd?
Prefixträd Snabbare Tar större plats Binära sökträd
Långsammare Tar mindre plats
Avvägning mellan tid och plats Hur ser ”växlingskursen” ut?
Olika tillämpningar har olika behov Stämmer det?
Sammanfattning
Prefixträd anses bra för lexikonhantering Bra att känna till som språkteknolog
Framtiden (för kursen)
Bara labben på måndag kvar!
Kursutvärdering kommer upp på hemsidan inom kort
Gör den!
Tveka inte att kontakta mig om ni undrar något
markus.saers@lingfil.uu.se