独自の再帰的実装またはスタックを使用した同等の反復を作成する場合、潜在的な問題があります。この例を見てください:
dic = {}
dic["key1"] = {}
dic["key1"]["key1.1"] = "value1"
dic["key2"] = {}
dic["key2"]["key2.1"] = "value2"
dic["key2"]["key2.2"] = dic["key1"]
dic["key2"]["key2.3"] = dic
通常の意味では、入れ子になった辞書はデータ構造のようなn-naryツリーになります。しかし、この定義は、クロスエッジまたはバックエッジ(したがって、もはやツリーではない)の可能性を排除していません。例えば、ここでkey2.2から辞書に保持KEY1、key2.3の全体の辞書を指し(バックエッジ/サイクル)。バックエッジ(サイクル)がある場合、スタック/再帰は無限に実行されます。
root<-------back edge
/ \ |
_key1 __key2__ |
/ / \ \ |
|->key1.1 key2.1 key2.2 key2.3
| / | |
| value1 value2 |
| |
cross edge----------|
Scharronからのこの実装でこの辞書を印刷する場合
def myprint(d):
for k, v in d.items():
if isinstance(v, dict):
myprint(v)
else:
print "{0} : {1}".format(k, v)
次のエラーが表示されます。
RuntimeError: maximum recursion depth exceeded while calling a Python object
sendleからの実装についても同様です。
同様に、Fred Fooからこの実装で無限ループを取得します。
def myprint(d):
stack = list(d.items())
while stack:
k, v = stack.pop()
if isinstance(v, dict):
stack.extend(v.items())
else:
print("%s: %s" % (k, v))
ただし、Pythonは実際にはネストされた辞書でサイクルを検出します。
print dic
{'key2': {'key2.1': 'value2', 'key2.3': {...},
'key2.2': {'key1.1': 'value1'}}, 'key1': {'key1.1': 'value1'}}
「{...}」は、サイクルが検出される場所です。
Moondraによって要求されたように、これはサイクル(DFS)を回避する方法です。
def myprint(d):
stack = list(d.items())
visited = set()
while stack:
k, v = stack.pop()
if isinstance(v, dict):
if k not in visited:
stack.extend(v.items())
else:
print("%s: %s" % (k, v))
visited.add(k)