[英]Python nested lists and recursion problem
我正在嘗試處理表示為python中的嵌套列表和字符串的一階邏輯公式,以使其處於析取范式,
即['&',['|','a','b'],['|','c','d']]
變成
['|' ['&',['&','a','c'],['&','b','c']],['&',['&','a','d '],['&','b','d']]]
在哪里 是“或”且&是“與”。
目前,我正在使用遞歸實現,該實現對公式進行多次傳遞,直到無法在“ and”的列表參數中找到任何嵌套的“或”符號。
這是我的實現,performDNF(form)是入口點。 現在,它對公式執行了一次遍歷,但是while循環檢查功能在'&'內找不到'|'並終止,請幫助任何使我發狂的人。
def dnfDistributivity(self, form):
if isinstance(form, type([])):
if len(form) == 3:
if form[0] == '&':
if form[1][0] == '|':
form = ['|', ['&', form[2], form[1][1]], ['&', form[2], form[1][2]]]
elif form[2][0] == '|':
form = ['|', ['&', form[1], form[2][1]], ['&', form[1], form[2][2]]]
form[1] = self.dnfDistributivity(form[1])
form[2] = self.dnfDistributivity(form[2])
elif len(form) == 2:
form[1] = self.dnfDistributivity(form[1])
return form
def checkDistributivity(self, form, result = 0):
if isinstance(form, type([])):
if len(form) == 3:
if form[0] == '&':
print "found &"
if isinstance(form[1], type([])):
if form[1][0] == '|':
return 1
elif isinstance(form[2], type([])):
if form[2][0] == '|':
return 1
else:
result = self.checkDistributivity(form[1], result)
print result
if result != 1:
result = self.checkDistributivity(form[2], result)
print result
elif len(form) == 2:
result = self.checkDistributivity(form[1], result)
print result
return result
def performDNF(self, form):
while self.checkDistributivity(form):
form = self.dnfDistributivity(self.dnfDistributivity(form))
return form
好的,這是一個可行的實際解決方案。
我不理解您的代碼,也沒有聽說過DNF,因此我首先研究問題。
DNF上的Wikipedia頁面非常有幫助。 它包括描述DNF的語法。
基於此,我編寫了一組簡單的遞歸函數,我相信它們可以正確識別您需要的格式的DNF。 我的代碼包括一些簡單的測試用例。
然后,我意識到數據表示形式的二叉樹性質使得通過編寫遞歸求反的函數negate()
來應用DeMorgan的定律來簡化“非”情況相對簡單,其余的就位。
我已經包含了測試用例。 它似乎有效。
我沒有進一步的計划。 如果有人發現錯誤,請提供一個測試用例,我將進行研究。
此代碼應在任何2.4版或更高版本的Python上運行。 您甚至可以通過使用簡單的字符列表替換Frozenset來將其移植到舊版Python。 我使用Python 3.x進行了測試,發現異常語法已更改,因此如果要在Python 3下運行它,則需要更改raise
行; 重要部分都起作用。
在這個問題,你沒有提到你用什么字符not
; 給您的使用&
對and
和|
對於or
,我認為您可能會使用!
為not
並相應地編寫代碼。 這是一份讓我為難,你的代碼的事情之一:永遠也不要指望找到not
在你的輸入?
我在這方面工作很有趣。 它不像數獨游戲那樣毫無意義。
import sys
ch_and = '&'
ch_not = '!'
ch_or = '|'
def echo(*args):
# like print() in Python 3 but works in 2.x or in 3
sys.stdout.write(" ".join(str(x) for x in args) + "\n")
try:
symbols = frozenset([ch_and, ch_not, ch_or])
except NameError:
raise Exception, "sorry, your Python is too old for this code"
try:
__str_type = basestring
except NameError:
__str_type = str
def is_symbol(x):
if not isinstance(x, __str_type) or len(x) == 0:
return False
return x[0] in symbols
def is_and(x):
if not isinstance(x, __str_type) or len(x) == 0:
return False
return x[0] == ch_and
def is_or(x):
if not isinstance(x, __str_type) or len(x) == 0:
return False
return x[0] == ch_or
def is_not(x):
if not isinstance(x, __str_type) or len(x) == 0:
return False
return x[0] == ch_not
def is_literal_char(x):
if not isinstance(x, __str_type) or len(x) == 0:
return False
return x[0] not in symbols
def is_list(x, n):
return isinstance(x, list) and len(x) == n
def is_literal(x):
"""\
True if x is a literal char, or a 'not' followed by a literal char."""
if is_literal_char(x):
return True
return is_list(x, 2) and is_not(x[0]) and is_literal_char(x[1])
def is_conjunct(x):
"""\
True if x is a literal, or 'and' followed by two conjuctions."""
if is_literal(x):
return True
return (is_list(x, 3) and
is_and(x[0]) and is_conjunct(x[1]) and is_conjunct(x[2]))
def is_disjunct(x):
"""\
True if x is a conjunction, or 'or' followed by two disjuctions."""
if is_conjunct(x):
return True
return (is_list(x, 3) and
is_or(x[0]) and is_disjunct(x[1]) and is_disjunct(x[2]))
def is_dnf(x):
return is_disjunct(x)
def is_wf(x):
"""returns True if x is a well-formed list"""
if is_literal(x):
return True
elif not isinstance(x, list):
raise TypeError, "only lists allowed"
elif len(x) == 2 and is_not(x[0]) and is_wf(x[1]):
return True
else:
return (is_list(x, 3) and (is_and(x[0]) or is_or(x[0])) and
is_wf(x[1]) and is_wf(x[2]))
def negate(x):
# trivial: negate a returns !a
if is_literal_char(x):
return [ch_not, x]
# trivial: negate !a returns a
if is_list(x, 2) and is_not(x[0]):
return x[1]
# DeMorgan's law: negate (a && b) returns (!a || !b)
if is_list(x, 3) and is_and(x[0]):
return [ch_or, negate(x[1]), negate(x[2])]
# DeMorgan's law: negate (a || b) returns (!a && !b)
if is_list(x, 3) and is_or(x[0]):
return [ch_and, negate(x[1]), negate(x[2])]
raise ValueError, "negate() only works on well-formed values."
def __rewrite(x):
# handle all dnf, which includes simple literals.
if is_dnf(x):
# basis case. no work to do, return unchanged.
return x
if len(x) == 2 and is_not(x[0]):
x1 = x[1]
if is_list(x1, 2) and is_not(x1[0]):
# double negative! throw away the 'not' 'not' and keep rewriting.
return __rewrite(x1[1])
assert is_list(x1, 3)
# handle non-inner 'not'
return __rewrite(negate(x1))
# handle 'and' with 'or' inside it
assert is_list(x, 3) and is_and(x[0]) or is_or(x[0])
if len(x) == 3 and is_and(x[0]):
x1, x2 = x[1], x[2]
if ((is_list(x1, 3) and is_or(x1[0])) and
(is_list(x2, 3) and is_or(x2[0]))):
# (a || b) && (c || d) -- (a && c) || (b && c) || (a && d) || (b && d)
lst_ac = [ch_and, x1[1], x2[1]]
lst_bc = [ch_and, x1[2], x2[1]]
lst_ad = [ch_and, x1[1], x2[2]]
lst_bd = [ch_and, x1[2], x2[2]]
new_x = [ch_or, [ch_or, lst_ac, lst_bc], [ch_or, lst_ad, lst_bd]]
return __rewrite(new_x)
if (is_list(x2, 3) and is_or(x2[0])):
# a && (b || c) -- (a && b) || (a && c)
lst_ab = [ch_and, x1, x2[1]]
lst_ac = [ch_and, x1, x2[2]]
new_x = [ch_or, lst_ab, lst_ac]
return __rewrite(new_x)
if (is_list(x1, 3) and is_or(x1[0])):
# (a || b) && c -- (a && c) || (b && c)
lst_ac = [ch_and, x1[1], x2]
lst_bc = [ch_and, x1[2], x2]
new_x = [ch_or, lst_ac, lst_bc]
return __rewrite(new_x)
return [x[0], __rewrite(x[1]), __rewrite(x[2])]
#return x
def rewrite(x):
if not is_wf(x):
raise ValueError, "can only rewrite well-formed lists"
while not is_dnf(x):
x = __rewrite(x)
return x
#### self-test code ####
__failed = False
__verbose = True
def test_not_wf(x):
global __failed
if is_wf(x):
echo("is_wf() returned True for:", x)
__failed = True
def test_dnf(x):
global __failed
if not is_wf(x):
echo("is_wf() returned False for:", x)
__failed = True
elif not is_dnf(x):
echo("is_dnf() returned False for:", x)
__failed = True
def test_not_dnf(x):
global __failed
if not is_wf(x):
echo("is_wf() returned False for:", x)
__failed = True
elif is_dnf(x):
echo("is_dnf() returned True for:", x)
__failed = True
else:
xr = rewrite(x)
if not is_wf(xr):
echo("rewrite produced non-well-formed for:", x)
echo("result was:", xr)
__failed = True
elif not is_dnf(xr):
echo("rewrite failed for:", x)
echo("result was:", xr)
__failed = True
else:
if __verbose:
echo("original:", x)
echo("rewritten:", xr)
echo()
def self_test():
a, b, c, d = 'a', 'b', 'c', 'd'
test_dnf(a)
test_dnf(b)
test_dnf(c)
test_dnf(d)
lstna = [ch_not, a]
test_dnf(lstna)
lstnb = [ch_not, b]
test_dnf(lstnb)
lsta = [ch_and, a, b]
test_dnf(lsta)
lsto = [ch_or, a, b]
test_dnf(lsto)
test_dnf([ch_and, lsta, lsta])
test_dnf([ch_or, lsta, lsta])
lstnn = [ch_not, [ch_not, a]]
test_not_dnf(lstnn)
test_not_dnf([ch_and, lstnn, lstnn])
# test 'and'/'or' inside 'not'
test_not_dnf([ch_not, lsta])
test_not_dnf([ch_not, lsto])
# test 'or' inside of 'and'
# a&(b|c) --> (a&b)|(b&c)
test_not_dnf([ch_and, a, [ch_or, b, c]])
# (a|b)&c --> (a&c)|(b&c)
test_not_dnf([ch_and, [ch_or, a, b], c])
# (a|b)&(c|d) --> ((a&c)|(b&c))|((a&d)|(b&d))
test_not_dnf([ch_and, [ch_or, a, b], [ch_or, c, d]])
# a&a&a&(b|c) --> a&a&(a&b|b&c) --> a&(a&a&b|a&b&c) --> (a&a&a&b|a&a&b&c)
test_not_dnf([ch_and, a, [ch_and, a, [ch_and, a, [ch_or, b, c]]]])
if __failed:
echo("one or more tests failed")
self_test()
現在,我很抱歉地說這句話,但是我對它的思考越多,我想你就越可能會讓我為你做功課。 因此,我只是編寫了此代碼的改進版本,但我不打算在這里共享它。 我將其作為練習留給您。 在描述完之后,您應該可以輕松地做到這一點。
我有一個while
循環重復調用__rewrite()
是一個可怕的技巧。 rewrite()
函數應該能夠通過一次調用__rewrite()
來重寫樹結構。 只需進行一些簡單的更改,您就可以擺脫while
循環; 我做到了,並對其進行了測試,並且有效。 您希望__rewrite()
沿着樹走下去,然后在備份的途中重寫內容, __rewrite()
工作。 您還可以修改__rewrite()
以在列表格式不正確時返回錯誤,並擺脫對is_wf()
的調用; 這也很容易。
我懷疑您的老師會在while
循環中停靠您的點,因此您應該有動力嘗試一下。 希望您樂在其中,也希望您從我的代碼中學到了有用的東西。
抱歉,我並不是真的想了解您的解決方案。 我認為這太難讀了,而且可能太麻煩了。
如果您有DNF,您要做的就是找到原子的所有組合,並從每個子列表中取出一個。 這幾乎可以歸結為這個問題……從每個“或”子句中,您都需要一個原子,並將所有與AND結合在一起。
所有這些可能的AND子句的OR組合都可產生您想要的結果。 對?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.