|
1 // DFAs and NFAs based on Scala's partial functions |
|
2 |
|
3 |
|
4 // edges of the DFAs and NFAs |
|
5 type Edge[A, C] = PartialFunction[(A, C), A] |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 // some states for test cases |
|
11 abstract class State |
|
12 case object Q0 extends State |
|
13 case object Q1 extends State |
|
14 case object Q2 extends State |
|
15 case object Q3 extends State |
|
16 case object Q4 extends State |
|
17 case object Q5 extends State |
|
18 case object Q6 extends State |
|
19 |
|
20 |
|
21 // class for DFAs |
|
22 case class DFA[A, C](start: A, // starting state |
|
23 trans: Edge[A, C], // transition |
|
24 fins: A => Boolean) { // final states |
|
25 |
|
26 // given a state and a character, |
|
27 // what is the next state, if there is any? |
|
28 def next(q: A, c: C) : Option[A] = |
|
29 trans.lift.apply((q, c)) |
|
30 |
|
31 // given a state and a string, what is the next state? |
|
32 def nexts(q: A, s: List[C]) : Option[A] = s match { |
|
33 case Nil => Some(q) |
|
34 case c::cs => next(q, c).flatMap(nexts(_, cs)) |
|
35 } |
|
36 |
|
37 // is a string accepted by an DFA? |
|
38 def accepts(s: List[C]) : Boolean = nexts(start, s) match { |
|
39 case None => false |
|
40 case Some(q) => fins(q) |
|
41 } |
|
42 |
|
43 } |
|
44 |
|
45 // DFA 1 |
|
46 val dtrans1 : Edge[State, Char] = |
|
47 { case (Q0, 'a') => Q0 |
|
48 case (Q0, 'b') => Q1 |
|
49 } |
|
50 |
|
51 val dfa1 = DFA(Q0, dtrans1, Set[State](Q1)) |
|
52 |
|
53 dfa1.accepts("aaab".toList) // true |
|
54 dfa1.accepts("aacb".toList) // false |
|
55 |
|
56 // another DFA test |
|
57 abstract class S |
|
58 case object S0 extends S |
|
59 case object S1 extends S |
|
60 case object S2 extends S |
|
61 case object Sink extends S |
|
62 |
|
63 // transition function |
|
64 // S0 --a--> S1 --a--> S2 |
|
65 val sigma : Edge[S, Char] = |
|
66 { case (S0, 'a') => S1 |
|
67 case (S1, 'a') => S2 |
|
68 case _ => Sink |
|
69 } |
|
70 |
|
71 val dfa1a = DFA(S0, sigma, Set[S](S2)) |
|
72 |
|
73 dfa1a.accepts("aa".toList) //true |
|
74 dfa1a.accepts("".toList) //false |
|
75 dfa1a.accepts("ab".toList) //false |
|
76 |
|
77 |
|
78 // class for NFAs |
|
79 case class NFA[A, C](starts: Set[A], // starting states |
|
80 trans: Set[Edge[A, C]], // transition edges |
|
81 fins: A => Boolean) { // final states |
|
82 |
|
83 // given a state and a character, what is the set of next states? |
|
84 def next(q: A, c: C) : Set[A] = |
|
85 trans.flatMap(_.lift.apply((q, c))) |
|
86 |
|
87 // given some states and a character, what is the set of next states? |
|
88 def nexts(qs: Set[A], c: C) : Set[A] = |
|
89 qs.flatMap(next(_, c)) |
|
90 |
|
91 // given some states and a string, what is the set of next states? |
|
92 def nextss(qs: Set[A], s: List[C]) : Set[A] = s match { |
|
93 case Nil => qs |
|
94 case c::cs => nextss(nexts(qs, c), cs) |
|
95 } |
|
96 |
|
97 // is a string accepted by an NFA? |
|
98 def accepts(s: List[C]) : Boolean = |
|
99 nextss(starts, s.toList).exists(fins) |
|
100 |
|
101 } |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 // NFA test cases |
|
107 |
|
108 // 1 |
|
109 val trans1 = Set[Edge[State, Char]]( |
|
110 { case (Q0, 'a') => Q1 }, |
|
111 { case (Q0, _) => Q0 }, |
|
112 { case (Q1, _) => Q2 }, |
|
113 { case (Q2, _) => Q3 }, |
|
114 { case (Q3, _) => Q4 }, |
|
115 { case (Q4, 'b') => Q5 }, |
|
116 { case (Q5, 'c') => Q6 } |
|
117 ) |
|
118 |
|
119 val nfa1 = NFA(Set[State](Q0), trans1, Set[State](Q6)) |
|
120 |
|
121 nfa1.accepts("axaybzbc".toList) // true |
|
122 nfa1.accepts("aaaaxaybzbc".toList) // true |
|
123 nfa1.accepts("axaybzbd".toList) // false |
|
124 |
|
125 |
|
126 |
|
127 // 2 |
|
128 val trans2 = Set[Edge[State, Char]]( |
|
129 { case (Q0, 'a') => Q0 }, |
|
130 { case (Q0, 'a') => Q1 }, |
|
131 { case (Q0, 'b') => Q2 }, |
|
132 { case (Q1, 'a') => Q1 }, |
|
133 { case (Q2, 'b') => Q2 } |
|
134 ) |
|
135 |
|
136 val nfa2 = NFA(Set[State](Q0), trans2, Set[State](Q2)) |
|
137 |
|
138 nfa2.accepts("aa".toList) // false |
|
139 nfa2.accepts("aaaaa".toList) // false |
|
140 nfa2.accepts("aaaaab".toList) // true |
|
141 nfa2.accepts("aaaaabbb".toList) // true |
|
142 nfa2.accepts("aaaaabbbaaa".toList) // false |
|
143 nfa2.accepts("ac".toList) // false |
|
144 |
|
145 // 3 |
|
146 val trans3 = Set[Edge[State, Char]]( |
|
147 { case (Q0, _) => Q0 }, |
|
148 { case (Q0, 'a') => Q1 }, |
|
149 { case (Q0, 'b') => Q3 }, |
|
150 { case (Q1, 'b') => Q2 }, |
|
151 { case (Q2, 'c') => Q5 }, |
|
152 { case (Q3, 'c') => Q4 }, |
|
153 { case (Q4, 'd') => Q5 } |
|
154 ) |
|
155 |
|
156 val nfa3 = NFA(Set[State](Q0), trans3, Set[State](Q5)) |
|
157 |
|
158 nfa3.accepts("aaaaabc".toList) // true |
|
159 nfa3.accepts("aaaabcd".toList) // true |
|
160 nfa3.accepts("aaaaab".toList) // false |
|
161 nfa3.accepts("aaaabc".toList) // true |
|
162 nfa3.accepts("aaaaabbbaaa".toList) // false |
|
163 |
|
164 |
|
165 |
|
166 // subset, or powerset, construction |
|
167 def powerset[A, C](nfa: NFA[A, C]) : DFA[Set[A], C] = { |
|
168 DFA(nfa.starts, |
|
169 { case (qs, c) => nfa.nexts(qs, c) }, |
|
170 _.exists(nfa.fins)) |
|
171 } |
|
172 |
|
173 val dfa2 = powerset(nfa1) |
|
174 |
|
175 dfa2.accepts("axaybzbc".toList) // true |
|
176 dfa2.accepts("aaaaxaybzbc".toList) // true |
|
177 dfa2.accepts("axaybzbd".toList) // false |
|
178 |
|
179 val dfa3 = powerset(nfa2) |
|
180 |
|
181 dfa3.accepts("aa".toList) // false |
|
182 dfa3.accepts("aaaaa".toList) // false |
|
183 dfa3.accepts("aaaaab".toList) // true |
|
184 dfa3.accepts("aaaaabbb".toList) // true |
|
185 dfa3.accepts("aaaaabbbaaa".toList) // false |
|
186 dfa3.accepts("ac".toList) // false |
|
187 |
|
188 |
|
189 import scala.annotation.tailrec |
|
190 @tailrec |
|
191 def fixpT[A](f: A => A, x: A): A = { |
|
192 val fx = f(x) |
|
193 if (fx == x) x else fixpT(f, fx) |
|
194 } |
|
195 |
|
196 |
|
197 case class eNFA[A, C](starts: Set[A], // starting state |
|
198 trans: Set[Edge[A, Option[C]]], // transition edges |
|
199 fins: A => Boolean) { // final states |
|
200 |
|
201 def enext(q: A) : Set[A] = |
|
202 trans.flatMap(_.lift.apply((q, None))) |
|
203 |
|
204 def enexts(qs: Set[A]) : Set[A] = |
|
205 qs ++ qs.flatMap(enext(_)) |
|
206 |
|
207 // epsilon closure |
|
208 def ecl(qs: Set[A]) : Set[A] = |
|
209 fixpT(enexts, qs) |
|
210 |
|
211 def next(q: A, c: C) : Set[A] = |
|
212 trans.flatMap(_.lift.apply((q, Some(c)))) |
|
213 |
|
214 def nexts(qs: Set[A], c: C) : Set[A] = |
|
215 qs.flatMap(next(_, c)) |
|
216 |
|
217 def nextss(qs: Set[A], s: List[C]) : Set[A] = s match { |
|
218 case Nil => ecl(qs) |
|
219 case c::cs => nextss(ecl(nexts(ecl(qs), c)), cs) |
|
220 } |
|
221 |
|
222 def accepts(s: List[C]) : Boolean = |
|
223 nextss(starts, s.toList).exists(fins) |
|
224 |
|
225 } |
|
226 |
|
227 val etrans1 = Set[Edge[State, Option[Char]]]( |
|
228 { case (Q0, Some('a')) => Q1 }, |
|
229 { case (Q1, None) => Q0 } |
|
230 ) |
|
231 |
|
232 val enfa = eNFA(Set[State](Q0), etrans1, Set[State](Q1)) |
|
233 |
|
234 enfa.accepts("a".toList) // true |
|
235 enfa.accepts("".toList) // false |
|
236 enfa.accepts("aaaaa".toList) // true |
|
237 enfa.accepts("aaaaab".toList) // flase |
|
238 enfa.accepts("aaaaabbb".toList) // false |
|
239 enfa.accepts("aaaaabbbaaa".toList) // false |
|
240 enfa.accepts("ac".toList) // false |
|
241 |
|
242 |
|
243 |
|
244 abstract class Rexp |
|
245 case object ZERO extends Rexp |
|
246 case object ONE extends Rexp |
|
247 case class CHAR(c: Char) extends Rexp |
|
248 case object ALL extends Rexp |
|
249 case class ALT(r1: Rexp, r2: Rexp) extends Rexp |
|
250 case class SEQ(r1: Rexp, r2: Rexp) extends Rexp |
|
251 case class STAR(r: Rexp) extends Rexp |
|
252 case class NTIMES(r: Rexp, n: Int) extends Rexp |
|
253 case class UPNTIMES(r: Rexp, n: Int) extends Rexp |
|
254 |
|
255 import scala.language.implicitConversions |
|
256 import scala.language.reflectiveCalls |
|
257 |
|
258 def charlist2rexp(s: List[Char]): Rexp = s match { |
|
259 case Nil => ONE |
|
260 case c::Nil => CHAR(c) |
|
261 case c::s => SEQ(CHAR(c), charlist2rexp(s)) |
|
262 } |
|
263 implicit def string2rexp(s: String): Rexp = charlist2rexp(s.toList) |
|
264 |
|
265 implicit def RexpOps (r: Rexp) = new { |
|
266 def | (s: Rexp) = ALT(r, s) |
|
267 def % = STAR(r) |
|
268 def ~ (s: Rexp) = SEQ(r, s) |
|
269 } |
|
270 |
|
271 implicit def stringOps (s: String) = new { |
|
272 def | (r: Rexp) = ALT(s, r) |
|
273 def | (r: String) = ALT(s, r) |
|
274 def % = STAR(s) |
|
275 def ~ (r: Rexp) = SEQ(s, r) |
|
276 def ~ (r: String) = SEQ(s, r) |
|
277 } |
|
278 |
|
279 |
|
280 |
|
281 // nullable function: tests whether the regular |
|
282 // expression can recognise the empty string |
|
283 def nullable (r: Rexp) : Boolean = r match { |
|
284 case ZERO => false |
|
285 case ONE => true |
|
286 case CHAR(_) => false |
|
287 case ALL => false |
|
288 case ALT(r1, r2) => nullable(r1) || nullable(r2) |
|
289 case SEQ(r1, r2) => nullable(r1) && nullable(r2) |
|
290 case STAR(_) => true |
|
291 case NTIMES(r, i) => if (i == 0) true else nullable(r) |
|
292 case UPNTIMES(r: Rexp, n: Int) => true |
|
293 } |
|
294 |
|
295 // derivative of a regular expression w.r.t. a character |
|
296 def der (c: Char, r: Rexp) : Rexp = r match { |
|
297 case ZERO => ZERO |
|
298 case ONE => ZERO |
|
299 case CHAR(d) => if (c == d) ONE else ZERO |
|
300 case ALL => ONE |
|
301 case ALT(r1, r2) => ALT(der(c, r1), der(c, r2)) |
|
302 case SEQ(r1, r2) => |
|
303 if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2)) |
|
304 else SEQ(der(c, r1), r2) |
|
305 case STAR(r1) => SEQ(der(c, r1), STAR(r1)) |
|
306 case NTIMES(r1, i) => |
|
307 if (i == 0) ZERO else |
|
308 if (nullable(r1)) SEQ(der(c, r1), UPNTIMES(r1, i - 1)) |
|
309 else SEQ(der(c, r1), NTIMES(r1, i - 1)) |
|
310 case UPNTIMES(r1, i) => |
|
311 if (i == 0) ZERO |
|
312 else SEQ(der(c, r1), UPNTIMES(r1, i - 1)) |
|
313 } |
|
314 |
|
315 |
|
316 // partial derivative of a regular expression w.r.t. a character |
|
317 def pder (c: Char, r: Rexp) : Set[Rexp] = r match { |
|
318 case ZERO => Set() |
|
319 case ONE => Set() |
|
320 case CHAR(d) => if (c == d) Set(ONE) else Set() |
|
321 case ALL => Set(ONE) |
|
322 case ALT(r1, r2) => pder(c, r1) ++ pder(c, r2) |
|
323 case SEQ(r1, r2) => |
|
324 (for (pr1 <- pder(c, r1)) yield SEQ(pr1, r2)) ++ |
|
325 (if (nullable(r1)) pder(c, r2) else Set()) |
|
326 case STAR(r1) => |
|
327 for (pr1 <- pder(c, r1)) yield SEQ(pr1, STAR(r1)) |
|
328 case NTIMES(r1, i) => |
|
329 if (i == 0) Set() else |
|
330 if (nullable(r1)) |
|
331 for (pr1 <- pder(c, r1)) yield SEQ(pr1, UPNTIMES(r1, i - 1)) |
|
332 else |
|
333 for (pr1 <- pder(c, r1)) yield SEQ(pr1, NTIMES(r1, i - 1)) |
|
334 case UPNTIMES(r1, i) => |
|
335 if (i == 0) Set() |
|
336 else |
|
337 for (pr1 <- pder(c, r1)) yield SEQ(pr1, UPNTIMES(r1, i - 1)) |
|
338 } |
|
339 |
|
340 val r = STAR(ALL) ~ "a" ~ NTIMES(ALL, 3) ~ "bc" |
|
341 val pder_nfa = NFA[Set[Rexp], Char](Set(Set(r)), |
|
342 Set( { case (rs, c) => rs.flatMap(pder(c, _))} ), |
|
343 _.exists(nullable)) |
|
344 |
|
345 |
|
346 |
|
347 pder_nfa.accepts("axaybzbc".toList) // true |
|
348 pder_nfa.accepts("aaaaxaybzbc".toList) // true |
|
349 pder_nfa.accepts("axaybzbd".toList) // false |
|
350 |
|
351 |
|
352 |
|
353 |
|
354 /// |
|
355 type Trans[A, C] = Map[(A, C), A] |
|
356 |