Skip to content

Instantly share code, notes, and snippets.

@tomca32
Created January 14, 2014 02:09
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 tomca32/8411855 to your computer and use it in GitHub Desktop.
Save tomca32/8411855 to your computer and use it in GitHub Desktop.
# Reading Machine Minds 2
#
# We say that a finite state machine is "empty" if it accepts no strings.
# Similarly, we say that a context-free grammar is "empty" if it accepts no
# strings. In this problem, you will write a Python procedure to determine
# if a context-free grammar is empty.
#
# A context-free grammar is "empty" starting from a non-terminal symbol S
# if there is no _finite_ sequence of rewrites starting from S that
# yield a sequence of terminals.
#
# For example, the following grammar is empty:
#
# grammar1 = [
# ("S", [ "P", "a" ] ), # S -> P a
# ("P", [ "S" ]) , # P -> S
# ]
#
# Because although you can write S -> P a -> S a -> P a a -> ... that
# process never stops: there are no finite strings in the language of that
# grammar.
#
# By contrast, this grammar is not empty:
#
# grammar2 = [
# ("S", ["P", "a" ]), # S -> P a
# ("S", ["Q", "b" ]), # S -> Q b
# ("P", ["P"]), # P -> P
# ("Q", ["c", "d"]), # Q -> c d
#
# And ["c","d","b"] is a witness that demonstrates that it accepts a
# string.
#
# Write a procedure cfgempty(grammar,symbol,visited) that takes as input a
# grammar (encoded in Python) and a start symbol (a string). If the grammar
# is empty, it must return None (not the string "None", the value None). If
# the grammar is not empty, it must return a list of terminals
# corresponding to a string in the language of the grammar. (There may be
# many such strings: you can return any one you like.)
#
# To avoid infinite loops, you should use the argument 'visited' (a list)
# to keep track of non-terminals you have already explored.
#
# Hint 1: Conceptually, in grammar2 above, starting at S is not-empty with
# witness [X,"a"] if P is non-empty with witness X and is non-empty with
# witness [Y,"b"] if Q is non-empty with witness Y.
#
# Hint 2: Recursion! A reasonable base case is that if your current
# symbol is a terminal (i.e., has no rewrite rules in the grammar), then
# it is non-empty with itself as a witness.
#
# Hint 3: all([True,False,True]) = False
# any([True,True,False]) = True
def cfgempty(grammar,symbol,visited):
# THIS IS MY ATTEMPT, NOT COMPLETE!
# def helper(symbol, visited):
# if not grammar or not symbol or symbol in visited: return None
# if symbol not in nonterms: return symbol
# branches = [branch for branch in grammar if branch[0] == symbol and not any(branch[1]) in visited]
# if not branches: return None
# res = []
# for branch in branches:
# res += [helper(sym, visited+[symbol]) for sym in branch[1]]
# if len(res) and (not res[-1] or not all(res)): res.pop()
# print "BRANCH RESULT:", res
# #res = [helper(sym, visited+[symbol]) for branch in branches for sym in branch[1]]
# return res
# nonterms = [x[0] for x in grammar]
# results = helper(symbol, visited)
# print 'RESULTS: ',results
# #print [result for result in results if len(result) and all(result)]
# return None
# We have provided a few test cases for you. You will likely want to add
# more of your own.
grammar1 = [
("S", [ "P", "a" ] ),
("P", [ "S" ]) ,
]
#print cfgempty(grammar1,"S",[])
print cfgempty(grammar1,"S",[]) == None
grammar2 = [
("S", ["P", "a" ]),
("S", ["Q", "b" ]),
("P", ["P"]),
("Q", ["c", "d"]),
]
#print cfgempty(grammar2,"S",[])
print cfgempty(grammar2,"S",[]) == ['c', 'd', 'b']
grammar3 = [ # some Spanish provinces
("S", [ "Barcelona", "P", "Huelva"]),
("S", [ "Q" ]),
("Q", [ "S" ]),
("P", [ "Las Palmas", "R", "Madrid"]),
("P", [ "T" ]),
("T", [ "T", "Toledo" ]),
("R", [ ]) ,
("R", [ "R"]),
]
print cfgempty(grammar3,"S",[]) == ['Barcelona', 'Las Palmas', 'Madrid', 'Huelva']
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment