Finite state machine
A deterministic finite state machine is a simple computation model, widely used as an introduction to automata theory in basic CS courses. It is a simple model, equivalent to regular expression, which determines of a certain input string is Accepted or Rejected. Leaving some formalities aside, A run of a finite state machine is composed of:
- alphabet, a set of characters.
- states, usually visualized as circles. One of the states must be the start state. Some of the states might be accepting, usually visualized as double circles.
- transitions, usually visualized as directed arches between states, are directed links between states associated with an alphabet letter.
- input string, a list of alphabet characters.
A run on the machine begins at the starting state. Each letter of the input string is read; If there is a transition between the current state and another state which corresponds to the letter, the current state is changed to the new state. After the last letter was read, if the current state is an accepting state, the input string is accepted. If the last state was not an accepting state, or a letter had no corresponding arch from a state during the run, the input string is rejected.
Note: This short descruption is far from being a full, formal definition of a FSM; Wikipedia's fine article is a great introduction to the subject.
Example
For example, the following machine tells if a binary number, read from left to right, has an even number of 0
s:
- The alphabet is the set
{0,1}
. - The states are S1 and S2.
- The transitions are
(S1, 0) -> S2
,(S1, 1) -> S1
,(S2, 0) -> S1
and(S2, 1) -> S2
. - The input string is any binary number, including an empty string.
The rules:
Implement a FSM in a language of your choice.
Input
The FSM should accept the following input:
<States> List of state, separated by space mark.
The first state in the list is the start state.
Accepting states begin with a capital letter.
<transitions> One or more lines.
Each line is a three-tuple:
origin state, letter, destination state)
<input word> Zero or more characters, followed by a newline.
For example, the aforementioned machine with 1001010
as an input string, would be written as:
S1 s2
S1 0 s2
S1 1 S1
s2 0 S1
s2 1 s2
1001010
Output
The FSM's run, written as <State> <letter> -> <state>
, followed by the final state. The output for the example input would be:
S1 1 -> S1
S1 0 -> s2
s2 0 -> S1
S1 1 -> S1
S1 0 -> s2
s2 1 -> s2
s2 0 -> S1
ACCEPT
For the empty input ''
:
S1
ACCEPT
Note: Following your comments, the S1
line (showing the first state) might be omitted, and the following output is also acceptable:
ACCEPT
For 101
:
S1 1 -> S1
S1 0 -> s2
s2 1 -> s2
REJECT
For '10X'
:
S1 1 -> S1
S1 0 -> s2
s2 X
REJECT
Prize
A 250 rep bounty will be given to the shortest solution.
Reference implementation
A reference Python implementation is available here. Note that output requirements have been relaxed for empty-string input.
Addendum
Output format
Following popular demand, the following output is also acceptable for empty input string:
ACCEPT
or
REJECT
Without the first state written in the previous line.
State names
Valid state names are an English letter followed by any number of letters, _
and digits, much like variable names, e.g. State1
, state0
, STATE_0
.
Input format
Like most code golfs, you can assume your input is correct.
Summary of answers:
- Cobol - 4078 characters
- Python - 171 characters, 568 characters, 203 characters, 218 characters, 269 characters
- sed - 137 characters
- ruby - 145 characters, 183 characters
- Haskell - 192 characters, 189 characters
- LISP - 725 characters
- Perl - 184 characters
- Bash - 184 characters
- Rexx - 205 characters
- Lua - 356 characters
- F# - 420 characters
- C# - 356 characters
- Mixal - 898 characters
The sed
137 solution is the shortest, ruby 145 is #2. Currently, I can't get the sed solution to work:
cat test.fsm | sed -r solution.sed
sed -r solution.sed test.fsm
both gave me:
sed: -e expression #1, char 12: unterminated `s' command
so unless It there are clarifications the bounty goes to the ruby solution.
Python (2.6) ~ 269 characters.
Probably still room for improvement, hints welcome. Handles specifications I think.
Python 2.7+,
201 192 187 181 179 175171 charsPS. After the problem was relaxed (no need to output state line on empty input), here is new code that's notably shorter. If you are on version <2.7, there is no dict comprehension, so instead of
{c+o:s for o,c,s in i[1:-1]}
trydict((c+o,s)for o,c,s in i[1:-1])
for the price of +5.And its test output:
Previous entry (len 201):
I want to apologize before someone slaps me for it: the code behavior is slightly different from the original spec - per question-comments discussion. This is my illustration for the discussion.
PS. while i like the resolution ACCEPT/REJECT on the same line with the final state, it can me moved to solitude (e.g. imagine results are to be parsed by stupid script that only cares about last line being accept or reject) by adding
'\n'+
(5 chars) to the lastprint
for the price of +5 chars.Example output:
F# 420
Not bad for immutable golf I think. I didn't do very good on the course today though.
33 lines for un-golfed F#. I'll update again in a bit after I've golfed.
C# -
453375353345 charactersThis doesn't win (not that anyone should have expected it to), but it was fun to write anyway. I kept the leading spaces and newlines for legibility:
In my last update I was able to save 22 characters by assuming a practical limit to the number of input rows (namely 99,999). In the worst case, you'd need to up that to the Int32 max of 2,147,483,647 which would add 5 chars. My machine doesn't like the idea of an array that long though...
An example of the execution:
MIXAL 898 characters
Deify Knuth!
Lua, 356
Takes any nonspace characters for states, and any non-space one characters for transition letters. Though it seems not shortest, I'll post it any way. Could save 25 chars printing tabs instead of spaces.
Readable version:
Golfed version + in- an output.