[英]defining multi branching nested tree in python recursively
我正在嘗試在Python中實現一些基本的遞歸結構,但沒有取得成功。 我有一棵以嵌套列表形式表示的樹,如下所示:
ex = ['A',
['A1',
['A11', 'tag'],
['A12', 'tag'],
['A13',
['A131', 'tag'],
['A132',
['A1321', 'tag'],
['A1322', 'tag']]]],
['A2', 'tag'],
['A3',
['A31',
['A311', 'tag'],
['A312', 'tag']],
['A32', 'tag'],
['A33',
['A331',
['A3311', 'tag'],
['A3312', 'tag']]],
['A34', 'tag'],
['A35',
['A351', 'tag'],
['A352',
['A3521', 'tag'],
['A3522', 'tag']]]],
['A4', 'tag']]
並且我定義了一個Node類,該類允許指定標簽'A', 'A1', ...
並添加子代。 可以通過注意children
不是列表來檢索終端節點。
class Node(object):
def __init__(self, tag, parent=None, children=[]):
self.tag = tag
self.parent = parent
self.children = children
self.is_terminal = False if isinstance(children, list) else True
def add_child(self, node):
if not isinstance(node, Node):
raise ValueError("Cannot append node of type: [%s]" % type(node))
if self.is_terminal:
raise ValueError("Cannot append node to terminal")
else:
self.children.append(node)
現在,我無法實現一種功能,該功能會將基於列表的樹遞歸轉換為基於節點的樹:
tree = Node(tag='A',
children=[Node(tag='A1',
children=[Node(tag='A11',
children='tag'),
Node(tag='A12',
children='tag'),
...]),
...])
到目前為止,這是我基於以下想法的嘗試:在嵌套列表中的每個位置,我們可能都有一個終端節點(在這種情況下,我們只是將其添加到根節點),或者是一個非終端節點,在這種情況下,我們提取了各自的根標簽並遞歸遍歷子級。 當列表為空時,我們將控制權返回給調用者。 我的感覺是編碼風格可能不是最適合Python,但我想更具體地了解我所缺少的內容。
def is_terminal(e):
return len(e) == 2 and type(e[0]) == str and type(e[1]) == str
def from_list(lst, root):
lst = list(lst) # avoid mutating input list
if not lst:
return
for e in lst:
if is_terminal(e):
tag, children = e
print "terminal", tag, "with root", root.tag
root.add_child(Node(tag=tag, children=children, parent=root))
else:
e = list(e)
tag, children = e.pop(0), e
print "non terminal", tag, "with root", root.tag
root = Node(tag=tag, parent=root)
from_list(children, root=root)
它有很多問題。 例如,它放寬了對最高根'A'
跟蹤-即A2
將A1
作為根。 它還將樹擴展為具有16個子節點的節點,每個終端節點一個,然后進行無限遞歸。 我將不勝感激任何提示。
我終於找到了問題,結果部分是算法中的遺漏點,部分是對Python列表工作方式的誤解。
該算法無法跟蹤最高根,因為我沒有在else
語句中添加子根。 此新版本解決了該問題。
else:
e = list(e)
tag, children = e.pop(0), e
print "non terminal", tag, "with root", root.tag
subroot = Node(tag=tag, parent=root)
root.add_child(subroot) #
from_list(children, root=subroot)
展平的問題實際上是我在Node
類定義中使用[]
作為默認參數的事實。 正如解釋在這里 ,默認的空列表正在創建只有第一個函數調用(或類實例化在這種情況下),而不是每次函數調用時。 因此,根的子項列表將被添加到所有子子項中(並因此產生扁平化效果),並且每次修改根子項列表時,所有子子項的子項列表都會被修改-因此無限遞歸。
事實證明,這更多的是Python陷阱,而不是算法定義問題。
為了記錄在案,代碼的完整更正版本:
class Node(object):
def __init__(self, tag, parent=None, children=None):
self.tag = tag
self.parent = parent
self.children = [] if not children else children
self.is_terminal = False if isinstance(self.children, list) else True
def add_child(self, node):
if not isinstance(node, Node):
raise ValueError("Cannot append node of type: [%s]" % type(node))
if self.is_terminal:
raise ValueError("Cannot append node to terminal")
else:
self.children.append(node)
def is_terminal(e):
return len(e) == 2 and type(e[0]) == str and type(e[1]) == str
def from_list(lst, root):
lst = list(lst)
if not lst:
return
for e in lst:
if is_terminal(e):
tag, children = e
print "terminal", tag, "with root", root.tag
root.add_child(Node(tag=tag, children=children, parent=root))
else:
e = list(e)
tag, children = e.pop(0), e
print "non terminal", tag, "with root", root.tag
newroot = Node(tag=tag, parent=root)
root.add_child(newroot)
from_list(children, root=newroot)
這是您的稱呼方式:
root = Node(tag=ex[0])
from_list(ex[1:], root=root)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.