I was working with students creating a simple demo of a finite state machine:
s2 = {}
s1 = {}
s1["0"] = s2
s1["1"] = s1
s2["0"] = s1
s2["1"] = s2
machine = {"start": s1, "accepting": [s1]}
def recognize(fsm, in_string):
return do_recognize(fsm, fsm["start"], in_string)
def do_recognize(fsm, current, in_string):
if len(in_string) == 0:
return current in fsm["accepting"]
return do_recognize(fsm, current[in_string[0]] ,
in_string[1:])
print (recognize(machine, "0"))
This machine recognizes strings with an even number of 0s, and it works fine on “good” strings (such as “1” or “010”). But on a “bad” string such as the one above, it gets
into an infinite loop and then stack overflow at return current in fsm[“accepting”].
I was able to determine that the problem is the comparison of the two states. In fact I can generate the exact same bug by just writing s1 == s2. But s1 == s1 (a good state) works fine.
My best guess of what’s happening is that it’s doing a deep compare and trying to follow all of the references in s2, which are circular. But why is it asymmetrical (i.e. why doesn’t s1 == s1 have the same problem)? And how can I avoid it?
When you compare dictionaries, each item (key/value pair) in the dictionary is compared as well, so if you have circular references between dictionaries where the circular references involve the same keys, you will get this maximum recursion depth exceeded error when comparing them:
For example, if you have
s1 == {'0': s2}ands2 == {'0': s1}, then attemptings1 == s2will result in the following comparisons, which illustrates how the recursion occurs:A containment test like
s1 in [s2]ors2 in [s1]will result in this equality comparison as well, which is why it happens in your code atcurrent in fsm["accepting"].You can work around this recursion issue by using an identity comparison instead of an equality comparison, just replace
current in fsm["accepting"]with the following:A better solution might be to not use circular references by having the states refer to an identifier instead of the object itself, for example you could have a structure like the following: