Skip to content

Instantly share code, notes, and snippets.

@vermiculus
Last active January 2, 2016 23:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vermiculus/8376562 to your computer and use it in GitHub Desktop.
Save vermiculus/8376562 to your computer and use it in GitHub Desktop.
A state machine implementing on/off functionality with a momentary-latch push-button.
from __future__ import print_function
class DeterministicFiniteAutomaton:
"""A simple DFA-ish class for Python.
Note that this does not implement accept states.
>>> dfa = DeterministicFiniteAutomaton('off', {
... ('on', 0): ('on', None),
... ('on', 1): ('on-off', None),
... ('on-off', 1): ('on-off', None),
... ('on-off', 0): ('off', lambda: print('turning off')),
... ('off', 0): ('off', None),
... ('off', 1): ('off-on', None),
... ('off-on', 1): ('off-on', None),
... ('off-on', 0): ('on', lambda: print('turning on'))
... })
>>> dfa.state
'off'
>>> dfa.read(1)
>>> dfa.state
'off-on'
>>> dfa.read(1)
>>> dfa.state
'off-on'
>>> dfa.read(0)
turning on
>>> dfa.state
'on'
>>> dfa.read('banana')
Traceback (most recent call last):
...
Exception: No transition from 'on' for letter 'banana'
"""
def __init__(self, initial_state, transition_table):
"""Creates a DFA"""
if initial_state not in map(lambda k: k[0], transition_table.keys()):
raise KeyError("Are you sure you don't have transitions from your initial state?")
self.state_old = None
self.state = initial_state
self.transitions = transition_table
def read(self, letter):
"""Read a letter and update the state, calling any appropriate
functions when the state has been updated."""
# See if we have a transition defined for this state on this
# letter; even if that transition is to itself.
if letter not in map(lambda k: k[1],
filter(lambda j: j[0] == self.state,
self.transitions.keys())):
raise Exception('No transition from {!r} for letter {!r}'.format(self.state, letter))
# Temporarily store the new state for ease of access
newstate = self.transitions[(self.state, letter)]
# Update state
self.oldstate = self.state
self.state = newstate[0]
# If we just moved to this state, call the edge function.
if self.oldstate != self.state and callable(newstate[1]):
newstate[1]()
# TODO: Edges from a state to itself are still edges and they
# should be callable. However, this is not important for any use
# case I can think of, since you generally don't want to *always*
# be doing something---only when you encounter a change.
def read_list(self, letters):
"""Read each letter in `letters' in order"""
map(self.read, letters)
import pprint
testdfa = DeterministicFiniteAutomaton('off', {
('on', 0): ('on', None),
('on', 1): ('on-off', None),
('on-off', 1): ('on-off', None),
('on-off', 0): ('off', lambda: pprint.pprint('off')),
('off', 0): ('off', None),
('off', 1): ('off-on', None),
('off-on', 1): ('off-on', None),
('off-on', 0): ('on', lambda: pprint.pprint('on'))
})
if __name__ == '__main__':
import doctest
doctest.testmod()
\documentclass[tikz]{standalone}
\usetikzlibrary{automata}
\begin{document}
\begin{tikzpicture}[shorten >=1pt,node distance=3cm,auto,thick]
\node [state, initial] (off) {Off};
\node [state] (on-off) [above right of=off] {};
\node [state] (off-on) [below right of=off] {};
\node [state] (on) [below right of=on-off] {On};
\path[->]
(off) edge [loop above] node {0} (off)
edge node {1} (off-on)
(off-on) edge [loop above] node {1} (off-on)
edge node {0} (on)
(on) edge [loop above] node {0} (on)
edge node {1} (on-off)
(on-off) edge [loop above] node {1} (on-off)
edge node {0} (off);
\end{tikzpicture}
\end{document}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment