| 121 |      1 | // NFAs and DFAs based on Scala's partial functions
 | 
|  |      2 | 
 | 
|  |      3 | 
 | 
|  |      4 | // (1) Write a polymorphic function that tests whether the 
 | 
|  |      5 | // intersection of two sets is non-empty
 | 
|  |      6 | 
 | 
|  |      7 | def share[A](a: Set[A], b: Set[A]) : Boolean = ...
 | 
|  |      8 | 
 | 
|  |      9 | 
 | 
|  |     10 | share(Set(1,2,3), Set(2, 3, 4))   // true
 | 
|  |     11 | share(Set(1,2,3), Set(4, 5, 6))   // false
 | 
|  |     12 | 
 | 
|  |     13 | 
 | 
|  |     14 | // State nodes of the DFAs and NFAs
 | 
|  |     15 | abstract class State
 | 
|  |     16 | type States = Set[State]
 | 
|  |     17 | 
 | 
|  |     18 | // Some states for test cases
 | 
|  |     19 | case object Q0 extends State
 | 
|  |     20 | case object Q1 extends State
 | 
|  |     21 | case object Q2 extends State
 | 
|  |     22 | case object Q3 extends State
 | 
|  |     23 | case object Q4 extends State
 | 
|  |     24 | case object Q5 extends State
 | 
|  |     25 | case object Q6 extends State
 | 
|  |     26 | 
 | 
|  |     27 | 
 | 
|  |     28 | // Transitions for DFAs and NFAs
 | 
|  |     29 | type Trans = PartialFunction[(State, Char), State]
 | 
|  |     30 | type NTrans = Set[Trans]
 | 
|  |     31 | 
 | 
|  |     32 | 
 | 
|  |     33 | // example transition of an DFA
 | 
|  |     34 | val dtrans : Trans = 
 | 
|  |     35 |   { case (Q0, 'a') => Q1 
 | 
|  |     36 |     case (Q0, 'b') => Q0
 | 
|  |     37 |     case (Q1, 'a') => Q2 
 | 
|  |     38 |     case (Q1, 'b') => Q0
 | 
|  |     39 |     case (Q2, 'a') => Q2 
 | 
|  |     40 |     case (Q2, 'b') => Q0 
 | 
|  |     41 |   }
 | 
|  |     42 | 
 | 
|  |     43 | 
 | 
|  |     44 | // (2) Write a function that takes a transition and a 
 | 
|  |     45 | // (state, character)-pair as arguments and produces an 
 | 
|  |     46 | // optional state (the state specified by the partial transition
 | 
|  |     47 | // function whenever it is defined; if the transition function 
 | 
|  |     48 | // is undefined, return None.
 | 
|  |     49 | 
 | 
|  |     50 | def fire(e: Trans, qc: (State, Char)) : Option[State] = ...
 | 
|  |     51 | 
 | 
|  |     52 | 
 | 
|  |     53 | // (3) Write a function that takes a transition, a state 
 | 
|  |     54 | // and a list of characters as arguments and produces 
 | 
|  |     55 | // the state generated by following the transitions for 
 | 
|  |     56 | // each character in the list.
 | 
|  |     57 | 
 | 
|  |     58 | def nexts(trans: Trans, q: State, s: List[Char]) : Option[State] = ...
 | 
|  |     59 | 
 | 
|  |     60 | 
 | 
|  |     61 | 
 | 
|  |     62 | // class for DFAs
 | 
|  |     63 | case class DFA(start: State,      // starting state
 | 
|  |     64 |                trans: Trans,      // transition
 | 
|  |     65 |                fins:  States)     // final states
 | 
|  |     66 | 
 | 
|  |     67 | // (4) Write a function that tests whether a string is accepted 
 | 
|  |     68 | // by an DFA or not.
 | 
|  |     69 | 
 | 
|  |     70 | def accepts(dfa: DFA, s: String) : Boolean = ...
 | 
|  |     71 | 
 | 
|  |     72 | 
 | 
|  |     73 | 
 | 
|  |     74 | // DFA examples
 | 
|  |     75 |  
 | 
|  |     76 | val dtrans1 : Trans = 
 | 
|  |     77 |   { case (Q0, 'a') => Q0 
 | 
|  |     78 |     case (Q0, 'b') => Q1 
 | 
|  |     79 |   }
 | 
|  |     80 | 
 | 
|  |     81 | val dfa1 = DFA(Q0, dtrans1, Set[State](Q1))
 | 
|  |     82 | 
 | 
|  |     83 | accepts(dfa1, "aaab")     // true
 | 
|  |     84 | accepts(dfa1, "aacb")     // false
 | 
|  |     85 | 
 | 
|  |     86 | 
 | 
|  |     87 | // NFAs
 | 
|  |     88 | 
 | 
|  |     89 | 
 | 
|  |     90 | // (5) Write a function that takes a transition set, a state
 | 
|  |     91 | // and a character as arguments, and calculates all possible 
 | 
|  |     92 | // next states (returned as set).
 | 
|  |     93 | 
 | 
|  |     94 | def nnext(trans: NTrans, q: State, c: Char) : States = ...
 | 
|  |     95 | 
 | 
|  |     96 | 
 | 
|  |     97 | // (6) Write a function that takes a transition set, a set of states 
 | 
|  |     98 | // and a character as arguments, and calculates all possible 
 | 
|  |     99 | // next states that can be reached from any state in the set.
 | 
|  |    100 | 
 | 
|  |    101 | def nnexts(trans: NTrans, qs: States, c: Char) : States = ...
 | 
|  |    102 | 
 | 
|  |    103 | 
 | 
|  |    104 | 
 | 
|  |    105 | // (7) Write a function that lifts nnexts from from single 
 | 
|  |    106 | // characters to lists of characters.
 | 
|  |    107 | def nnextss(trans: NTrans, qs: States, s: List[Char]) : States = ...
 | 
|  |    108 | 
 | 
|  |    109 | 
 | 
|  |    110 | // class for NFAs
 | 
|  |    111 | case class NFA(start: States,       // starting state
 | 
|  |    112 |                trans: NTrans,       // transition edges
 | 
|  |    113 |                fins:  States)       // final states
 | 
|  |    114 | 
 | 
|  |    115 | 
 | 
|  |    116 | // (8) Write a function that tests whether a string is 
 | 
|  |    117 | // accepted by an NFA or not.
 | 
|  |    118 | 
 | 
|  |    119 | def naccepts(nfa: NFA, s: String) : Boolean = ...
 | 
|  |    120 | 
 | 
|  |    121 | 
 | 
|  |    122 | // (9) Write similar functions as in (7) and (8), but instead of 
 | 
|  |    123 | // returning states or a boolean, calculate the number of states 
 | 
|  |    124 | // that need to be followed in each step.
 | 
|  |    125 | 
 | 
|  |    126 | def max_nextss(trans: NTrans, qs: States, s: List[Char], max: Int) : Int = ...
 | 
|  |    127 | 
 | 
|  |    128 | def max_accepts(nfa: NFA, s: String) : Int = ...
 | 
|  |    129 | 
 | 
|  |    130 | 
 | 
|  |    131 | // NFA examples
 | 
|  |    132 | 
 | 
|  |    133 | 
 | 
|  |    134 | // 1 
 | 
|  |    135 | val trans1 : NTrans = Set(
 | 
|  |    136 |   { case (Q0, 'a') => Q1 },
 | 
|  |    137 |   { case (Q0, _)   => Q0 },
 | 
|  |    138 |   { case (Q1, _)   => Q2 },
 | 
|  |    139 |   { case (Q2, _)   => Q3 },
 | 
|  |    140 |   { case (Q3, _)   => Q4 },
 | 
|  |    141 |   { case (Q4, 'b') => Q5 },
 | 
|  |    142 |   { case (Q5, 'c') => Q6 }
 | 
|  |    143 | )
 | 
|  |    144 | 
 | 
|  |    145 | val nfa1 = NFA(Set[State](Q0), trans1, Set[State](Q6))
 | 
|  |    146 | 
 | 
|  |    147 | naccepts(nfa1, "axaybzbc")     // true
 | 
|  |    148 | naccepts(nfa1, "aaaaxaybzbc")  // true
 | 
|  |    149 | naccepts(nfa1, "axaybzbd")     // false
 | 
|  |    150 | 
 | 
|  |    151 | // the nfa has five states, which might be all 
 | 
|  |    152 | // active
 | 
|  |    153 | 
 | 
|  |    154 | max_accepts(nfa1, "axaybzbc")     // 3 
 | 
|  |    155 | max_accepts(nfa1, "aaaaxaybzbc")  // 5
 | 
|  |    156 | max_accepts(nfa1, "axaybzbd")     // 3
 | 
|  |    157 | max_accepts(nfa1, "aaaaaaaaaaaaaxaybzbd")   // 5
 | 
|  |    158 | 
 | 
|  |    159 | 
 | 
|  |    160 | // 2
 | 
|  |    161 | val trans2 : NTrans = Set(
 | 
|  |    162 |   { case (Q0, 'a') => Q0 },
 | 
|  |    163 |   { case (Q0, 'a') => Q1 },
 | 
|  |    164 |   { case (Q0, 'b') => Q2 },
 | 
|  |    165 |   { case (Q1, 'a') => Q1 },
 | 
|  |    166 |   { case (Q2, 'b') => Q2 }
 | 
|  |    167 | )
 | 
|  |    168 | 
 | 
|  |    169 | val nfa2 = NFA(Set[State](Q0), trans2, Set[State](Q2))
 | 
|  |    170 | 
 | 
|  |    171 | naccepts(nfa2, "aa")             // false
 | 
|  |    172 | naccepts(nfa2, "aaaaa")          // false
 | 
|  |    173 | naccepts(nfa2, "aaaaab")         // true
 | 
|  |    174 | naccepts(nfa2, "aaaaabbb")       // true
 | 
|  |    175 | naccepts(nfa2, "aaaaabbbaaa")    // false
 | 
|  |    176 | naccepts(nfa2, "ac")             // false
 | 
|  |    177 | 
 | 
|  |    178 | // 3
 | 
|  |    179 | val trans3 : NTrans = Set(
 | 
|  |    180 |   { case (Q0, _)   => Q0 },
 | 
|  |    181 |   { case (Q0, 'a') => Q1 },
 | 
|  |    182 |   { case (Q0, 'b') => Q3 },
 | 
|  |    183 |   { case (Q1, 'b') => Q2 },
 | 
|  |    184 |   { case (Q2, 'c') => Q5 },
 | 
|  |    185 |   { case (Q3, 'c') => Q4 },
 | 
|  |    186 |   { case (Q4, 'd') => Q5 }
 | 
|  |    187 | )
 | 
|  |    188 | 
 | 
|  |    189 | val nfa3 = NFA(Set[State](Q0), trans3, Set[State](Q5))
 | 
|  |    190 | 
 | 
|  |    191 | naccepts(nfa3, "aaaaabc")      // true
 | 
|  |    192 | naccepts(nfa3, "aaaabcd")      // true
 | 
|  |    193 | naccepts(nfa3, "aaaaab")       // false
 | 
|  |    194 | naccepts(nfa3, "aaaabc")       // true
 | 
|  |    195 | naccepts(nfa3, "aaaaabbbaaa")  // false
 | 
|  |    196 | 
 | 
|  |    197 | 
 |