CSS 343: Notes from Lecture 16 (DRAFT)

Administrivia

Iterators

Finite-State Automata

Cocktail-Party Computer Science

For those who attend the kind of party where they talk about this kind of thing.

Example: Floating-Point Number Recognizer

Problem: converting a string to a floating point number. The practical solution is to make the appropriate library call. To understand how to do it, do it yourself at least once.

Finite-state automaton to recognize floating-point numbers. [Finite-State Floating-Point Recognizer]

Observe that we may produce the following equivalent automaton with one-fewer state by combining states s4 and s5: [Finite-State Floating-Point Recognizer]

Example: Microwave Oven

Legal states and transitions in tabular form:

State Event
OPEN_DOOR CLOSE_DOOR SET_TIMER PRESS_START PRESS_CANCEL DING!
1
(closed, unset, off)
7 invalid 3 1 1 invalid
2
(closed, unset, on)
invalid
3
(closed, set, off)
9 invalid 3 6 1 invalid
4
(closed, unset, on)
invalid
5
(closed, running, off)
invalid (simple microwave)
6
(closed, running, on)
9 invalid 6 6 3 1
7
(open, unset, off)
invalid 1 9 7 7 invalid
8
(closed, unset, on)
invalid
9
(open, set, off)
invalid 3 9 9 7 invalid
10
(open, set, on)
invalid
11
(open, running, off)
invalid
12
(open, running, on)
invalid

State-transition diagram: [Finite-State Model of a Micowave Oven]

Implementation

There are several ways to implement a state machine. Choices depend on the particular requirements and tradeoffs between flexibility and effiency.

The simplest implementation uses a switch statement:

State get_next_state(State s, Event e) {
   switch(s) {
      case STATE_CLOSED_UNSET_OFF:
         switch(e) {
            case EVENT_OPEN_DOOR:
               return STATE_OPEN_UNSET_OFF;
            case EVENT_SET_TIMER:
               return STATE_CLOSED_SET_OFF;
            case EVENT_PRESS_START:
            case EVENT_PRESS_CANCEL:
               return STATE_CLOSED_UNSET_OFF;
            default:
               cerr << "invalid event" << e << " in state " <<s "\n";
         }
         break;
         //...
   }
}
      

Another approach is to use a data structure, either a graph or a lookup table. As a lookup table, you may have something like this:

class Machine {
   public:
      Machine();
      //...
      State get_next_state(State s, Event e);
   private:
      map<State, map<Event, State>*> state_map_;
};

Machine::Machine() {
  map<Event, State>* event_map = new map<Event, State>
  state_map_[STATE_CLOSED_UNSET_OFF] = event_map
  (*event_map)[EVENT_OPEN_DOOR] = STATE_OPEN_UNSET_OFF;
  (*event_map)[EVENT_SET_TIMER] = STATE_CLOSED_SET_OFF;
  (*event_map)[EVENT_PRESS_START] = STATE_CLOSED_UNSET_OFF;
  (*event_map)[EVENT_PRESS_CANCEL] = STATE_CLOSED_UNSET_OFF;
  //...
}

Machine::get_next_state(State s, Event e) {
   return (*state_map[s])[e];
}