progs/automata_sol.scala
changeset 121 4fc05d4f0e01
equal deleted inserted replaced
120:564b4baec85e 121:4fc05d4f0e01
       
     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   !(a intersect b).isEmpty
       
     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   e.lift.apply(qc)
       
    52 
       
    53 
       
    54 // (3) Write a function that takes a transition, a state 
       
    55 // and a list of characters as arguments and produces 
       
    56 // the state generated by following the transitions for 
       
    57 // each character in the list.
       
    58 
       
    59 def nexts(trans: Trans, q: State, s: List[Char]) : Option[State] = s match {
       
    60   case Nil => Some(q)
       
    61   case c::cs => fire(trans, (q, c)).flatMap(nexts(trans, _, cs))
       
    62 }
       
    63 
       
    64 
       
    65 
       
    66 // class for DFAs
       
    67 case class DFA(start: State,      // starting state
       
    68                trans: Trans,      // transition
       
    69                fins:  States)     // final states
       
    70 
       
    71 // (4) Write a function that tests whether a string is accepted 
       
    72 // by an DFA or not.
       
    73 
       
    74 def accepts(dfa: DFA, s: String) : Boolean = nexts(dfa.trans, dfa.start, s.toList) match {
       
    75   case None => false
       
    76   case Some(q) => dfa.fins contains q
       
    77 }
       
    78 
       
    79 
       
    80 // DFA examples
       
    81  
       
    82 val dtrans1 : Trans = 
       
    83   { case (Q0, 'a') => Q0 
       
    84     case (Q0, 'b') => Q1 
       
    85   }
       
    86 
       
    87 val dfa1 = DFA(Q0, dtrans1, Set[State](Q1))
       
    88 
       
    89 accepts(dfa1, "aaab")     // true
       
    90 accepts(dfa1, "aacb")     // false
       
    91 
       
    92 
       
    93 // NFAs
       
    94 
       
    95 
       
    96 // (5) Write a function that takes a transition set, a state
       
    97 // and a character as arguments, and calculates all possible 
       
    98 // next states (returned as set).
       
    99 
       
   100 def nnext(trans: NTrans, q: State, c: Char) : States = {
       
   101   trans.map(fire(_, (q, c))).flatten
       
   102 }
       
   103 
       
   104 // (6) Write a function that takes a transition set, a set of states 
       
   105 // and a character as arguments, and calculates all possible 
       
   106 // next states that can be reached from any state in the set.
       
   107 
       
   108 def nnexts(trans: NTrans, qs: States, c: Char) : States = {
       
   109   qs.flatMap(nnext(trans, _, c))
       
   110 }
       
   111 
       
   112 
       
   113 // (7) Write a function that lifts nnexts from from single 
       
   114 // characters to lists of characters.
       
   115 def nnextss(trans: NTrans, qs: States, s: List[Char]) : States = s match {
       
   116   case Nil => qs
       
   117   case c::cs => {
       
   118     val ns = nnexts(trans, qs, c)
       
   119     nnextss(trans, ns, cs) 
       
   120   }
       
   121 }
       
   122 
       
   123 // class for NFAs
       
   124 case class NFA(start: States,       // starting state
       
   125                trans: NTrans,       // transition edges
       
   126                fins:  States)       // final states
       
   127 
       
   128 
       
   129 // (8) Write a function that tests whether a string is 
       
   130 // accepted by an NFA or not.
       
   131 
       
   132 def naccepts(nfa: NFA, s: String) : Boolean = {
       
   133   share(nnextss(nfa.trans, nfa.start, s.toList), nfa.fins)
       
   134 }
       
   135 
       
   136 
       
   137 // (9) Write similar functions as in (7) and (8), but instead of 
       
   138 // returning states or a boolean, calculate the number of states 
       
   139 // that need to be followed in each step.
       
   140 
       
   141 def max_nextss(trans: NTrans, qs: States, s: List[Char], max: Int) : Int = s match {
       
   142   case Nil => max
       
   143   case c::cs => {
       
   144     val ns = nnexts(trans, qs, c)
       
   145     val ns_size = ns.size
       
   146     if (max < ns_size) max_nextss(trans, ns, cs, ns_size) 
       
   147     else max_nextss(trans, ns, cs, max)
       
   148   }
       
   149 }
       
   150 
       
   151 def max_accepts(nfa: NFA, s: String) : Int = {
       
   152   max_nextss(nfa.trans, nfa.start, s.toList, 0)
       
   153 }
       
   154 
       
   155 
       
   156 // NFA examples
       
   157 
       
   158 
       
   159 // 1 
       
   160 val trans1 : NTrans = Set(
       
   161   { case (Q0, 'a') => Q1 },
       
   162   { case (Q0, _)   => Q0 },
       
   163   { case (Q1, _)   => Q2 },
       
   164   { case (Q2, _)   => Q3 },
       
   165   { case (Q3, _)   => Q4 },
       
   166   { case (Q4, 'b') => Q5 },
       
   167   { case (Q5, 'c') => Q6 }
       
   168 )
       
   169 
       
   170 val nfa1 = NFA(Set[State](Q0), trans1, Set[State](Q6))
       
   171 
       
   172 naccepts(nfa1, "axaybzbc")     // true
       
   173 naccepts(nfa1, "aaaaxaybzbc")  // true
       
   174 naccepts(nfa1, "axaybzbd")     // false
       
   175 
       
   176 // the nfa has five states, which might be all 
       
   177 // active
       
   178 
       
   179 max_accepts(nfa1, "axaybzbc")     // 3 
       
   180 max_accepts(nfa1, "aaaaxaybzbc")  // 5
       
   181 max_accepts(nfa1, "axaybzbd")     // 3
       
   182 max_accepts(nfa1, "aaaaaaaaaaaaaxaybzbd")   // 5
       
   183 
       
   184 
       
   185 // 2
       
   186 val trans2 : NTrans = Set(
       
   187   { case (Q0, 'a') => Q0 },
       
   188   { case (Q0, 'a') => Q1 },
       
   189   { case (Q0, 'b') => Q2 },
       
   190   { case (Q1, 'a') => Q1 },
       
   191   { case (Q2, 'b') => Q2 }
       
   192 )
       
   193 
       
   194 val nfa2 = NFA(Set[State](Q0), trans2, Set[State](Q2))
       
   195 
       
   196 naccepts(nfa2, "aa")             // false
       
   197 naccepts(nfa2, "aaaaa")          // false
       
   198 naccepts(nfa2, "aaaaab")         // true
       
   199 naccepts(nfa2, "aaaaabbb")       // true
       
   200 naccepts(nfa2, "aaaaabbbaaa")    // false
       
   201 naccepts(nfa2, "ac")             // false
       
   202 
       
   203 // 3
       
   204 val trans3 : NTrans = Set(
       
   205   { case (Q0, _)   => Q0 },
       
   206   { case (Q0, 'a') => Q1 },
       
   207   { case (Q0, 'b') => Q3 },
       
   208   { case (Q1, 'b') => Q2 },
       
   209   { case (Q2, 'c') => Q5 },
       
   210   { case (Q3, 'c') => Q4 },
       
   211   { case (Q4, 'd') => Q5 }
       
   212 )
       
   213 
       
   214 val nfa3 = NFA(Set[State](Q0), trans3, Set[State](Q5))
       
   215 
       
   216 naccepts(nfa3, "aaaaabc")      // true
       
   217 naccepts(nfa3, "aaaabcd")      // true
       
   218 naccepts(nfa3, "aaaaab")       // false
       
   219 naccepts(nfa3, "aaaabc")       // true
       
   220 naccepts(nfa3, "aaaaabbbaaa")  // false
       
   221 
       
   222