| 
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  | 
  !(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  | 
  |