-1

フォーマット文字列の辞書があれば、カスケード/再帰的な文字列補間を行いたいです。

FOLDERS = dict(home="/home/user",
               workspace="{home}/workspace",
               app_project="{workspace}/{app_name}",
               app_name="my_app")

私はこの実装から始めました:

def interpolate(attrs):
    remain = [k for k, v in attrs.items() if "{" in v]
    while remain:
        for k in remain:
            attrs[k] = attrs[k].format(**attrs)
        remain = [k for k in remain if "{" in attrs[k]]

このinterpolate()関数は、最初にフォーマット文字列を選択します。次に、フォーマット文字列がなくなるまで文字列を置き換えます。

次の Python 辞書でこの関数を呼び出すと、次のようになります。

>>> import pprint
>>> pprint.pprint(FOLDERS)
{'app_name': 'my_app',
 'app_project': '/home/user/workspace/my_app',
 'home': '/home/user',
 'workspace': '/home/user/workspace'}

結果は OK ですが、この実装では参照サイクルが検出されません。

たとえば、次の呼び出しは無限ループになります。

>>> interpolate({'home': '{home}'})

誰かが私により良い実装を与えることができますか?

編集:解決策

レオンの解決策は優れていてシンプルだと思います。セルジュ・ベレスタの解決策も同様です。

私はそれをそのように実装します:

def interpolate(attrs):
    remain = [k for k, v in attrs.items() if "{" in v]
    while remain:
        for k in remain:
            attrs[k] = attrs[k].format(**attrs)
            fmt = '{' + k + '}'
            if fmt in attrs[k]: # check for reference cycles
                raise ValueError("Reference cycle found for '{k}'!".format(k=k))
        remain = [k for k in remain if "{" in attrs[k]]
4

2 に答える 2

1

このような参照サイクルは for ループで簡単に確認できます。for ループ内の一致する値でキーが参照されているかどうかを確認するだけです。

def interpolate(attrs):
    remain = [k for k, v in attrs.items() if "{" in v]
    while remain:
        for k in remain:
            attrs[k] = attrs[k].format(**attrs)
            if '{%s}' % k in attrs[k]: # check for reference cycles
                raise ValueError("Input contains at least one reference cycle!")
        remain = [k for k in remain if "{" in attrs[k]]

参照サイクルがある場合、エラーが発生するようになりました。これは、1 つが見つかるか、すべての置換が完了するまで置換されるため、任意の長さの参照サイクルを検出します。

于 2016-06-14T12:35:39.563 に答える
0

無限ループを回避するために循環参照を検出することが唯一の問題である場合は、1 つの補間が入力を返すとすぐに停止できます。

def interpolate(attrs):
    remain = [k for k, v in attrs.items() if "{" in v]
    while remain:
        for k in remain:
            attrs[k] = attrs[k].format(**attrs)
        temp = [k for k in remain if "{" in attrs[k]]
        if temp == remain:
            # cyclic reference detected
            ...
        remain = temp
于 2016-06-14T12:38:33.127 に答える