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.
Ruby 1.9.2 - 178 190 182 177 153 161 158 154 145 characters
h={}
o=s=p
$<.map{|l|o,b,c=l.split;h[[o,b]]=c;s||=o}
o.chars{|c|puts s+' '+c+((s=h[[s,c]])?' -> '+s :'')}rescue 0
puts s&&s<'['?:ACCEPT: :REJECT
Testing Script
[
"S1 s2
S1 0 s2
S1 1 S1
s2 0 S1
s2 1 s2
1001010",
"S1 s2
S1 0 s2
S1 1 S1
s2 0 S1
s2 1 s2
101",
"S1 s2
S1 0 s2
S1 1 S1
s2 0 S1
s2 1 s2
",
"S1 s2
S1 0 s2
S1 1 S1
s2 0 S1
s2 1 s2
10X"
].each do |b|
puts "------"
puts "Input:"
puts b
puts
puts "Output:"
puts `echo "#{b}" | ruby fsm-golf.rb`
puts "------"
end
Outputs
All input starts with:
S1 s2
S1 0 s2
S1 1 S1
s2 0 S1
s2 1 s2
Input: '1001010'
Output:
S1 1 -> S1
S1 0 -> s2
s2 0 -> S1
S1 1 -> S1
S1 0 -> s2
s2 1 -> s2
s2 0 -> S1
ACCEPT
Input: '101'
Output:
S1 1 -> S1
S1 0 -> s2
s2 1 -> s2
REJECT
Input: 'X10'
Output:
S1 X
REJECT
Input: ''
Output:
ACCEPT
Input: '10X'
Output:
S1 1 -> S1
S1 0 -> s2
s2 X
REJECT
Python 2.7+, 201 192 187 181 179 175 171 chars
PS. 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]}
try dict((c+o,s)for o,c,s in i[1:-1])
for the price of +5.
import sys
i=map(str.split,sys.stdin)
s=i[0][0]
for c in''.join(i[-1]):
if s:o=s;s={c+o:s for o,c,s in i[1:-1]}.get(c+s,());print o,c,'->',s
print'ARCECJEEPCTT'[s>'Z'::2]
And its test output:
# for empty input
ACCEPT
# for input '1001010'
S1 1 -> S1
S1 0 -> s2
s2 0 -> S1
S1 1 -> S1
S1 0 -> s2
s2 1 -> s2
s2 0 -> S1
ACCEPT
# for input '101'
S1 1 -> S1
S1 0 -> s2
s2 1 -> s2
REJECT
# for input '10X'
S1 1 -> S1
S1 0 -> s2
s2 X -> ()
REJECT
# for input 'X10'
S1 X -> ()
REJECT
Previous entry (len 201):
import sys
i=list(sys.stdin)
s=i[0].split()[0]
t={}
for r in i[1:-1]:a,b,c=r.split();t[a,b]=c
try:
for c in i[-1]:print s,c.strip(),;s=t[s,c];print' ->',s
except:print('ACCEPT','REJECT')[s>'Z'or' '<c]
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 last print
for the price of +5 chars.
Example output:
# for empty input
S1 ACCEPT
# for input '1001010'
S1 1 -> S1
S1 0 -> s2
s2 0 -> S1
S1 1 -> S1
S1 0 -> s2
s2 1 -> s2
s2 0 -> S1
S1 ACCEPT
# for input '101'
S1 1 -> S1
S1 0 -> s2
s2 1 -> s2
s2 REJECT
# for input '10X'
S1 1 -> S1
S1 0 -> s2
s2 X REJECT
# for input 'X10'
S1 X REJECT
I'm feeling retro today, my language of choice for this task is IBM Enterprise Cobol - char count 2462 4078 (Sorry, pasted from a screen oriented device, trailing spaces are a tragic side effect):
Identification Division.
Program-ID. FSM.
Environment Division.
Data Division.
Working-Storage Section.
01 FSM-Storage.
*> The current state
05 Start-State Pic X(2).
05 Next-State Pic X(2).
*> List of valid states
05 FSM-State-Cnt Pic 9.
05 FSM-States Occurs 9
Pic X(2).
*> List of valid transitions
05 FSM-Trans-Cnt Pic 999.
05 FSM-Trans Occurs 999.
10 Trans-Start Pic X(2).
10 Trans-Char Pic X.
10 Trans-End Pic X(2).
*> Input
05 In-Buff Pic X(72).
*> Some work fields
05 II Pic s9(8) binary.
05 JJ Pic s9(8) binary.
05 Wk-Start Pic X(2).
05 Wk-Char Pic X.
05 Wk-End Pic X(2).
05 Wk-Cnt Pic 999.
05 Pic X value ' '.
88 Valid-Input value 'V'.
05 Pic X value ' '.
88 Valid-State value 'V'.
05 Pic X value ' '.
88 End-Of-States value 'E'.
05 Pic X value ' '.
88 Trans-Not-Found value ' '.
88 Trans-Found value 'T'.
Linkage Section.
01 The-Char-Area.
05 The-Char Pic X.
88 End-Of-Input value x'13'.
05 The-Next-Char Pic X.
Procedure Division.
Perform Load-States
Perform Load-Transitions
Perform Load-Input
Perform Process-Input
Goback.
*> Run the machine...
Process-Input.
Move FSM-States (1) to Start-State
Set address of The-Char-Area to address of In-Buff
Perform until End-Of-Input
Perform Get-Next-State
Set address of The-Char-Area to address of The-Next-Char
Move Next-State to Start-State
End-Perform
If Start-State (1:1) is Alphabetic-Lower
Display 'REJECT'
Else
Display 'ACCEPT'
End-If
Exit.
*> Look up the first valid transition...
Get-Next-State.
Set Trans-Not-Found to true
Perform varying II from 1 by 1
until (II > FSM-State-Cnt)
or Trans-Found
If Start-State = Trans-Start (II)
and The-Char = Trans-Char (II)
Move Trans-End (II) to Next-State
Set Trans-Found to true
End-If
End-Perform
Display Start-State ' ' The-Char ' -> ' Next-State
Exit.
*> Read the states in...
Load-States.
Move low-values to In-Buff
Accept In-Buff from SYSIN
Move 0 to FSM-State-Cnt
Unstring In-Buff
delimited by ' '
into FSM-States (1) FSM-States (2) FSM-States (3)
FSM-States (4) FSM-States (5) FSM-States (6)
FSM-States (7) FSM-States (8) FSM-States (9)
count in FSM-State-Cnt
End-Unstring
Exit.
*> Read the transitions in...
Load-Transitions.
Move low-values to In-Buff
Accept In-Buff from SYSIN
Perform varying II from 1 by 1
until End-Of-States
Move 0 to Wk-Cnt
Unstring In-Buff
delimited by ' '
into Wk-Start Wk-Char Wk-End
count in Wk-Cnt
End-Unstring
If Wk-Cnt = 3
Add 1 to FSM-Trans-Cnt
Move Wk-Start to Trans-Start (FSM-Trans-Cnt)
Move Wk-Char to Trans-Char (FSM-Trans-Cnt)
Move Wk-End to Trans-End (FSM-Trans-Cnt)
Move low-values to In-Buff
Accept In-Buff from SYSIN
Else
Set End-Of-States to true
End-If
End-Perform
Exit.
*> Fix input so it has newlines...the joys of mainframes
Load-Input.
Perform varying II from length of In-Buff by -1
until Valid-Input
If In-Buff (II:1) = ' ' or In-Buff (II:1) = low-values
Move x'13' to In-Buff (II:1)
Else
Set Valid-Input to true
End-If
End-Perform
Exit.
End Program FSM.
sed -- 118 137 characters
This is using the -r flag (+3), for a total of 134+3=137 characters.
$!{H;D}
/:/!{G;s/(\S*)..(\S*)/\2 \1:/}
s/(.* .)(.*\n\1 (\S*))/\1 -> \3\n\3 \2/
/-/{P;D}
/^[A-Z].* :/cACCEPT
s/( .).*/\1/
/:/!P
cREJECT
This should handle inputs without transitions correctly... hopefully it fully complies with the spec now...
Adam provided a reference implementation. I didn't see it before I made mine, but the logic is similar:
Edit: This is Python 2.6 code. I did not try to minimize length; I just tried to make it conceptually simple.
import sys
a = sys.stdin.read().split('\n')
states = a[0].split()
transitions = a[1:-2]
input = a[-2]
statelist = {}
for state in states:
statelist[state] = {}
for start, char, end in [x.split() for x in transitions]:
statelist[start][char] = end
state = states[0]
for char in input:
if char not in statelist[state]:
print state,char
print "REJECT"
exit()
newstate = statelist[state][char]
print state, char, '->', newstate
state = newstate
if state[0].upper() == state[0]:
print "ACCEPT"
else:
print "REJECT"
Python, 218 characters
import sys
T=sys.stdin.read()
P=T.split()
S=P[0]
n="\n"
for L in P[-1]if T[-2]!=n else"":
i=T.find(n+S+" "+L)
if i<0:print S,L;S=n;break
S=T[i:].split()[2];print S,L,"->",S
print ("REJECT","ACCEPT")[S[0].isupper()]
Haskell - 252 216 204 197 192 characters
s%(c:d,t)=s++' ':c:maybe('\n':x)(\[u]->" -> "++u++'\n':u%(d,t))(lookup[s,[c]]t)
s%_|s<"["="ACCEPT\n"|1<3=x
x="REJECT\n"
p(i:j)=(words i!!0)%(last j,map(splitAt 2.words)j)
main=interact$p.lines
Conforms to output specification.
Ungolf'd version:
type State = String
type Transition = ((State, Char), State)
run :: [Transition] -> State -> String -> [String]
run ts s (c:cs) = maybe notFound found $ lookup (s,c) ts
where
notFound = stateText : ["REJECT"]
found u = (stateText ++ " -> " ++ u) : run ts u cs
stateText = s ++ " " ++ [c]
run _ (s:_) "" | s >= 'A' && s <= 'Z' = ["ACCEPT"]
| otherwise = ["REJECT"]
prepAndRun :: [String] -> [String]
prepAndRun (l0:ls) = run ts s0 input
where
s0 = head $ words l0
input = last ls
ts = map (makeEntry . words) $ init ls
makeEntry [s,(c:_),t] = ((s,c),t)
main' = interact $ unlines . prepAndRun . lines
A good puzzle is why init
isn't needed in the golf'd version! Other than that, rest are all standard Haskell golf techniques.
Perl — 184 characters
(Count excluding all newlines, which are optional.)
($s)=split' ',<>;$\=$/;
while(<>){chomp;$r{$_[1].$_[0]}=$_[2]if split>2;$t=$_}
$_=$t;
1 while$s&&s/(.)(.*)/print"$s $1",($s=$r{$1.$s})?" -> $s":"";$2/e;
print$s=~/^[A-Z]/?"ACCEPT":"REJECT"
Also, this 155-character program does not implement the intermediate outputs, but executes the machine entirely as a repeated substitution on the whole FSM definition (changing the start state and input string). It was inspired by, but not derived from, the sed
solution. It could be shortened by 2 characters by converting the (?:...)
into a (...)
and renumbering as needed.
$/="";$_=<>;
1 while s/\A(\S+)(?: +\S+)*\n(.*\n)?\1 +(.) +(.+)\n(.*\n)?\3([^\n]*)\n\z/$4\n$2$1 $3 $4\n$5$6\n/s;
print/\A[A-Z].*\n\n\z/s?"ACCEPT\n":"REJECT\n"
Python 3, Chars: 203
The output format seems a bit hard to fit.
import sys
L=[i.split()for i in sys.stdin]
N,P=L[0][0],print
for c in L[-1]and L[-1][-1]:
if N:O,N=N,{(i[0],i[1]):i[2]for i in L[1:-1]}.get((N,c),'');P(O,c,N and'-> '+N)
P(('REJECT','ACCEPT')[''<N<'_'])
MIXAL 898 characters
ORIG 3910
A ALF ACCEP
ALF T
ORIG 3940
R ALF REJEC
ALF T
ORIG 3970
O CON 0
ALF ->
ORIG 3000
S ENT6 0
T IN 0,6(19)
INC6 14
JBUS *(19)
LDA -14,6
JANZ T
LDA -28,6(9)
DECA 30
JAZ C
DECA 1
JANZ B
C LD2 0(10)
ENT4 -28,6
ENT5 9
D JMP G
ENT3 0
F INC3 14
LD1 0,3(10)
DEC2 0,1
J2Z M
INC2 0,1
DEC3 -28,6
J3NN U
INC3 -28,6
JMP F
M INC2 0,1
LD1 0,3(36)
DECA 0,1
JAZ H
INCA 0,1
JMP F
H INCA 0,1
ST2 O(10)
LD2 1,3(10)
STA O(36)
ST2 O+1(37)
OUT O(18)
JBUS *(18)
JMP D
HLT
E LD1 0(10)
DEC1 0,2
J1Z B
U OUT R(18)
JBUS *(18)
HLT
B OUT A(18)
JBUS *(18)
HLT
G STJ K
ST5 *+1(36)
LDA 0,4
JAZ E
DECA 30
JAZ I
DECA 1
JANZ W
INCA 1
I INCA 30
DEC5 45
J5NN J
INC5 54
JMP K
J INC4 1
ENT5 9
K JMP *
W ST2 O(10)
INCA 31
STA O(36)
STZ O+1
OUT O(18)
JBUS *(18)
JMP B
END S
Deify Knuth!
Haskell - 189 characters
main=interact$r.lines
r f=g t z$last f where{(z:_):t=map words f;g _ s""|s<"["="ACCEPT\n";g([q,j,p]:_)s(i:k)|i:s==j++q=s++' ':i:" -> "++p++'\n':g t p k;g(_:y)s i=g y s i;g _ _ _="REJECT\n"}
EDIT: Does not correctly implement the output for no-transition rejection.
Line-broken version and variable guide:
-- r: run FSM
-- f: fsm definition as lines
-- z: initial state
-- g: loop function
-- t: transition table
-- s: current state
-- i: current input
-- k: rest of input
-- q: transition table match state
-- j: transition table match input
-- p: transition table next state
-- y: tail of transition table
main=interact$r.lines;
r f=g t z$last f where{
(z:_):t=map words f;
g _ s""|s<"["="ACCEPT\n";
g([q,j,p]:_)s(i:k)|i:s==j++q=s++' ':i:" -> "++p++'\n':g t p k;
g(_:y)s i=g y s i;
g _ _ _="REJECT\n"}
I got the s<"["
technique from MtnViewMark's solution; the rest is my own design. Notable characteristics:
- The input is left as junk in the transition table. This is OK as long as the input does not contain two spaces; but note that the transition rule format is arguably unfriendly to transitioning on the space character anyway.
- Stepping through the input string and searching the transition table are the same function.
- Both REJECT cases are handled by the same fallthrough.
Common Lisp - 725
(defun split (string)
(loop for i = 0 then (1+ j)
as j = (position #\Space string :start i)
collect (subseq string i j)
while j))
(defun do-fsm ()
(let* ((lines (loop for l = (read-line *standard-input* nil)
until (not l)
collect (split l)))
(cur (caar lines))
(transitions (subseq lines 1 (- (length lines) 1))))
(if (or (loop for c across (caar (last lines))
do (format t "~a ~a" cur c)
when (not (loop for tr in transitions
when (and (equal cur (car tr))
(equal c (char (cadr tr) 0)))
return (progn (format t " -> ~a~%"
(setq cur (caddr tr)))
t)
))
return t)
(lower-case-p (char cur 0)))
(format t "~%REJECT~%")
(format t "ACCEPT~%"))))
No real attempt to minimize the code -- Common Lisp pays a heavy penalty in the required input processing, so I don't think there's much chance of this solution winning :-)
Ruby — 183
h={}
r=$<.read
t=s=r.split[0]
i=r[-1]=="
"?"":r.split[-1]
r.scan(/(\S+) (.) (.+)/){|a,b,c|h[[a,b]]=c}
i.chars{|c|puts s+" #{c} -> #{s=h[[s,c]]}"}
puts s&&s[/^[A-Z]/]?"ACCEPT":"REJECT"
Really, strange output specification. Here how my works: http://ideone.com/cxweL
Rexx 205 characters
(This answer went through few edits as I initially just posted some code for general interest and then decided to actually post a real solution)
Here's a Rexx version to give people a taste for that less known lanugage. Rexx http://en.wikipedia.org/wiki/REXX is an interpreted language used in IBM's VM/CMS mainframe operating system and later in IBM OS/2 (and I believe there was an Amiga variant). It's a very expressive language and an amazing general purpose/"scripting" language.
Parse pull i .
d.='~'
Do until l='';Parse pull o l d.o.l;End
Do j=1 to LENGTH(o)
t=SUBSTR(o,j,1);p=i t;i=d.i.t
If i=d. then Do;Say p;Leave;End
Say p '->' i
End
Say WORD('ACCEPT REJECT',c2d(left(i,1))%32-1)
This can be run with the Regina Rexx interpreter.
Handling the incorrect transition scenario with its unique output and also testing for uppercase is a bit expensive.
Code from some older edits below for people interested in the Rexx syntax, those aren't 100% compliant with the output requirements but are functional (all code in this answer works with the samples I pasted below but the code above handles the other required corners):
Older short version:
Parse pull i .
Do until l = ""; Parse pull o l d; t.o.l = d; End
Do j=1 to LENGTH(o); t=substr(o,j,1); Say i t "->" t.i.t; i=t.i.t; End
If LEFT(i,1)='S' then Say 'ACCEPT'; else say 'REJECT'
Longer version:
Parse pull initial . /* Rexx has a powerful built in string parser, this takes the first word into initial */
Do until letter = "" /* This style of do loops is a bit unusual, note how it doesn't matter that letter isn't defined yet */
Parse pull origin letter destination /* Here we parse the inpt line into three words */
transition.origin.letter = destination /* Rexx has a very powerful notion of associative containers/dictionaries, many years pre-Python */
End
/* Now we take the last line and iterate over the transitions */
Do i = 1 to LENGTH(origin)
t = substr(origin, i, 1) /* This is the actual letter using Rexx's string functions */
Say initial t "->" transition.initial.t /* Say is like print */
initial = transition.initial.t /* Perform the transition */
End
/* check for uppercase in the current state */
if left(initial, 1) = 'S' then Say 'ACCEPT'; else say 'REJECT'
Sample in/out:
S1 s2
S1 0 s2
0
S1 0 -> s2
REJECT
S1 s2
S1 0 s2
S1 1 S1
s2 0 S1
s2 1 s2
1001010
S1 1 -> S1
S1 0 -> s2
s2 0 -> S1
S1 1 -> S1
S1 0 -> s2
s2 1 -> s2
s2 0 -> S1
ACCEPT
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:
i=io.read
p=print
S={}
i():gsub("(%S+)",function (a) f=f or a S[a]={} end )
l=i"*a"
for a,t,d in l:gmatch"(%S+) (%S) (%S+)"do
S[a][t]=d
end
I=l:match"(%S+)%s$"or"" -- fixes empty input
function F(a,i)
t=I:sub(i,i)
if t==""then
p"ACCEPT"
elseif S[a][t] then
p(("%s %s -> %s"):format(a,t, S[a][t]))
return F( S[a][t],i+1)
else
if t~=""then p(a.." "..t)end p'REJECT'
end
end
F(f,1)
Golfed version + in- an output.
i=io.read p=print S={}i():gsub('(%S+)',function(a)f=f or a S[a]={}end)l=i'*a'for a,t,d in l:gmatch"(%S+) (%S) (%S+)"do S[a][t]=d end I=l:match'(%S+)%s$'or''function F(a,i)t=I:sub(i,i)if t==""and a:match'^%u'then p'ACCEPT'elseif S[a][t]then p(('%s %s -> %s'):format(a,t,S[a][t]))return F(S[a][t],i+1)else if t~=''then p(a.." "..t)end p'REJECT'end end F(f,1)
-- input --
A B C
A B B
A C C
A A A
B A A
B B B
B C C
C A A
C B B
C C C
AABCCBCBAX
-- output --
A A -> A
A A -> A
A B -> B
B C -> C
C C -> C
C B -> B
B C -> C
C B -> B
B A -> A
REJECT
bash - 186 185 184 chars
declare -A a read s x while read f m t&&[ $m ];do a[$f $m]=$t;done for((i=0;i-${#f};i++))do b="$s ${f:i:1}";s=${a[$b]};echo $b -\> $s;done [ "$s" = "${s,}" ]&&echo REJECT||echo ACCEPT
Note that this does actually require bash - POSIX sh doesn't have associative arrays or the C-style for syntax (and probably doesn't have all the parameter expansions used either, although I haven't checked).
Edit: alternatively, for the exact same length,
declare -A a read s x while read f m t&&[ $m ];do a[$f $m]=$t;done while [ $f ];do b="$s ${f:i:1}";f=${f:1};s=${a[$b]};echo $b -\> $s;done [ "$s" = "${s,}" ]&&echo REJECT||echo ACCEPT
Python (2.6) ~ 269 characters.
Probably still room for improvement, hints welcome. Handles specifications I think.
import sys;a=sys.stdin.readlines();b=a[0].split()
f=b[0];d=dict((x,{})for x in b);s=''
for x,y,z in map(str.split,a[1:-1]):d[x][y]=z
for g in a[-1]:
try:s+=f+' '+g;f=d[f][g];s+=' -> '+f+'\n'
except:s+='\n';break
print s+("REJECT","ACCEPT")[ord(f[0])<90 and g in d[f]]
Lua - 248 227
r=...
p=print
M={}
s=r:match('(%a%d)')
for i,n,o in r:gmatch('(%a%d)%s(%d)%s(%a%d)')do
M[i]=M[i]or{}
M[i][n]=o
end
for c in r:match('%d%d+'):gmatch('(%d)')do
z=s
s=M[z][c]
p(z,c,'->',s)
end
p(s==s:upper()and'ACCEPT'or'REJECT')
check running version on codepad old version
F# 420
Not bad for immutable golf I think. I didn't do very good on the course today though.
open System
let f,p,a=Array.fold,printf,Map.add
let l=Console.In.ReadToEnd().Split '\n'
let e,s=l.Length,l.[0].Split ' '
let t,w=Map.ofList[for q in s->q,Map.empty],[|"ACCEPT";"REJECT"|]
let m=f(fun t (r:String)->let s=r.Split ' 'in a s.[0](t.[s.[0]]|>a s.[1].[0]s.[2])t)t l.[1..e-2]
try let r=l.[e-1].ToCharArray()|>f(fun s c->p"%s %c "s c;let n=m.[s].[c]in p"-> %s\n"n;n)s.[0]in p"%s"w.[int r.[0]/97]with|_->p"%s"w.[1]
33 lines for un-golfed F#. I'll update again in a bit after I've golfed.
open System
let input = Console.In.ReadToEnd()
//let input = "S1 s2\nS1 0 s2\nS1 1 S1\ns2 0 S1\ns2 1 s2\n1001010"
let lines = input.Split '\n'
let length = lines.Length
let states = lines.[0].Split ' '
let stateMap = Map.ofList [for state in states -> (state, Map.empty)]
let folder stateMap (line:String) =
let s = line.Split ' '
stateMap |> Map.add s.[0] (stateMap.[s.[0]] |> Map.add s.[1].[0] s.[2])
let machine = Array.fold folder stateMap lines.[1 .. (length-2)]
let stateMachine state char =
printf "%s %c " state char
let newState = machine.[state].[char]
printfn "-> %s" newState
newState
try
let result =
lines.[length-1].ToCharArray()
|> Array.fold stateMachine states.[0]
if Char.IsUpper result.[0] then
printf "ACCEPT"
else
printf "REJECT"
with
| _ -> printf "REJECT"
C# - 453 375 353 345 characters
This 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:
using System;
class P
{
static void Main()
{
string c,k="";
var t=new string[99999][];
int p=-1,n;
while((c=Console.ReadLine())!="")
t[++p]=c.Split(' ');
c=t[0][0];
foreach(var d in t[p][0]){
k+=c+' '+d;
for(n=1;n<p;n++)
if(c==t[n][0]&&d==t[n][1][0])
{
c=t[n][2];
k+=" -> "+c;
break;
}
k+="\n";
if(n==p){
c="~";
break;
}
}
Console.Write(k+(c[0]>'Z'?"REJECT":"ACCEPT"));
}
}
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:
>FSM.exe
S1 s2
S1 0 s2
S1 1 S1
s2 0 S1
s2 1 s2
1001010
S1 1 -> S1
S1 0 -> s2
s2 0 -> S1
S1 1 -> S1
S1 0 -> s2
s2 1 -> s2
s2 0 -> S1
ACCEPT
来源:https://stackoverflow.com/questions/4661818/code-golf-finite-state-machine