This term, I was teaching a course on intro to theory of computation, and one of the topics was finite automatons — DFAs, NFAs, and the like. I began by writing down the definition of a DFA:
A deterministic finite automaton (DFA) is a 5-tuple where: is a finite set of states…
I could practically feel my students falling asleep in their seats. Inevitably, a student asked the one question you should never ask a theorist:
“So… how is this useful in real life?”
DFAs as a model of computation
I’ve done some theoretical research on formal language theory and DFAs, so my immediate response was why DFAs are important to theorists.
Above: A DFA requires O(1) memory, regardless of the length of the input.
You might have heard of Turing machines, which abstracts the idea of a “computer”. In a similar vein, regular languages describe what is possible to do with a computer with very little memory. No matter how long the input is, a DFA only keeps track of what state it’s currently in, so it only requires a constant amount of memory.
By studying properties of regular languages, we gain a better understanding of what is and what isn’t possible with computers with very little memory.
This explains why theorists care about regular languages — but what are some real world applications?
DFAs and regular expressions
Regular expressions are a useful tool that every programmer should know. If you wanted to check if a string is a valid email address, you might write something like:
Behind the scenes, this regular expression gets converted into an NFA, which can be quickly evaluated to produce an answer.
You don’t need to understand the internals of this in order to use regular expressions, but it’s useful to know some theory so you understand its limitations. Some programmers may try to use regular expressions to parse HTML, but if you’ve seen the Pumping Lemma, you will understand why this is fundamentally impossible.
DFAs in compilers
In every programming language, the first step in the compiler or interpreter is the lexer. The lexer reads in a file of your favorite programming language, and produces a sequence of tokens. For example, if you have this line in C++:
cout << "Hello World" << endl;
The lexer generates something like this:
IDENTIFIER cout LSHIFT << STRING "Hello World" LSHIFT << IDENTIFIER endl SEMICOLON ;
The lexer uses a DFA to go through the source file, one character at a time, and emit tokens. If you ever design your own programming language, this will be one of the first things you will write.
Above: Lexer description for JSON numbers, like -3.05
DFAs for artificial intelligence
Another application of finite automata is programming simple agents to respond to inputs and produce actions in some way. You can write a full program, but a DFA is often enough to do the job. DFAs are also easier to reason about and easier to implement.
The AI for Pac-Man uses a four-state automaton:
Typically this type of automaton is called a Finite State Machine (FSM) rather than a DFA. The difference is that in a FSM, we do an action depending on the state, whereas in a DFA, we care about accepting or rejecting a string — but they’re the same concept.
DFAs in probability
What if we took a DFA, but instead of fixed transition rules, the transitions were probabilistic? This is called a Markov Chain!
Above: 3 state Markov chain to model the weather
Markov chains are frequently used in probability and statistics, and have lots of applications in finance and computer science. Google’s PageRank algorithm uses a giant Markov chain to determine the relative importance of web pages!
You can calculate things like the probability of being in a state after a certain number of time steps, or the expected number of steps to reach a certain state.
In summary, DFAs are powerful and flexible tools with myriad real-world applications. Research in formal language theory is valuable, as it helps us better understand DFAs and what they can do.