1

変数の存在を決してテストしてはならないことを読みました。プログラムで変数が存在するかどうかを確認する必要がある場合、「変数を認識」していないため、設計エラーになります。ただし、関数の呼び出しごとに辞書とリストに値を追加する再帰関数があります。グローバル変数の宣言を避けるために、変数を関数に対してローカルにしようとしています。しかし、そのためには、関数の先頭で myList と myDict を [] と {} として宣言する必要があります。もちろん、これにより、以前の再帰呼び出しで dict と list に加えた変更が消去されますが、これは望ましくありません。最初に try ... catch を挿入し、変数の存在を確認し、まだ存在しない場合にのみ {} と [] として宣言することを考えましたが、それは悪い設計であると読みました。これにアプローチするより良い方法はありますか?実際のコードを添付していないことをお詫びしますが、この関数はまだ計画の初期段階にあるため、添付するものはあまりありません。

4

3 に答える 3

1

再帰関数で変更可能な状態に本当にアクセスする必要がある場合は、おそらくクラスを使用する必要があります。

このような状況では、オブジェクト指向プログラミング (OOP) をサポートしない言語を使用する場合、多くの場合、名前空間の衝突を避けるために独自のモジュール内に含まれている可能性があるグローバル変数を使用するのが最善の方法です。グローバル変数は「悪い」ものですが、時には 2 つ (またはそれ以上) の悪よりも小さいものです。しかし、Python でコードを書いているので、豊富な OOP 構造のセットが提供されます。それらを使用する必要があります。

たとえば、メモ化を使用して単純なバージョンの O(2^n) の複雑さを回避する単純な再帰フィボナッチ関数を次に示します (先頭のアンダースコアは、名前が「プライベート」であることを意味するだけであり、詳細な知識なしでは使用しないでください)。その内部の仕組みの):

class _Fib(object):
    def __init__(self):
        self.cache = {0:0, 1:1}
    def __call__(self, n):
        if n not in self.cache:
            self.cache[n] = self(n - 1) + self(n - 2)
        return self.cache[n]

fib = _Fib()

簡単なテスト; 2 番目の呼び出しは、認識できる遅延なしで返されることに注意してください。

>>> map(fib, xrange(10))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
>>> fib(249)
4880197746793002076754294951020699004973287771475874L

この「関数」(実際には呼び出し可能なオブジェクト) は、作成時に変更可能なオブジェクトを作成することで、説明した問題を解決することがわかります。次に、 を介して可変オブジェクトにアクセスしますself。上記よりも優れた書き方がありますがfib、これは、状態を必要とするより複雑な再帰関数の基本的な設計として適している可能性があります。

もちろん、他にも多くのオプションがあります。別の回答で述べられているように、変更可能なデフォルトを使用することは非常に似ていますが、より明示的であるため、クラスを使用することを好みます。オブジェクトを関数の属性として保存することもできます!

def foo(a, b):
    if base_case(a, b):
        return
    foo.dct[a] = b
    foo.lst.append((a, b))
    foo(a - 1, b - 1)
foo.dct = {}
foo.lst = []

私はこの構造が好きではありませんが、知っておく価値はあります。最後に、最も明白な解決策を完全に無視しないでください。

def foo(a, b, dct, lst):
    if base_case(a, b):
        return
    dct[a] = b
    lst.append((a, b))
    foo(a - 1, b - 1, dct, lst)

場合によっては、これが実際に最善の方法です。それは確かに単純明快です。

最後に、関数スコープの性質を誤解しているように見えることを確認します。あなたは言う

最初に try ... catch を挿入し、変数の存在を確認し、まだ存在しない場合にのみ {} と [] として宣言することを考えましたが、それは悪い設計であると読みました。

これは悪くないデザインです。これは壊れた設計です。動作しません。辞書とリストがグローバルでない場合関数の先頭ではローカル変数が定義されていないため、常にtry/except例外が発生します。

これは私を最終的な考えに導きます。おそらく、関数が最初に呼び出されるたびに新しい辞書とリストを作成し、再帰が停止した後にそれらを破棄したいでしょう。その場合、上記のクラスベースのオプションを微調整する必要があります。または、再帰関数を、呼び出されるたびに新しく作成する外部関数でラップすることもできます。

def outer(a, b):
    return _inner_recursive(a, b, {}, [])

def _inner_recursive(a, b, lst, dct):
    #blah blah blah
于 2012-09-24T01:38:06.327 に答える
1

ここで利用できるpythonの「落とし穴」があります。デフォルトの変更可能な引数 (辞書やリストなど) は、再帰的に変更されます。

def my_function(d={}):
    ...
    #recursion will mutate d

注意して使用する必要がありますが、便利です。

.

簡単な例:

def f(a,d=[]):
    d.append(a)
    if a!=2:
        f(2)
    return d

print f(1) # [1,2]
于 2012-09-23T22:12:38.577 に答える
1

新しいローカル変数を作成しても、以前の呼び出しのローカル変数は上書きされません。関数を呼び出すたびに、新しいローカル変数が取得されます。関数が自分自身を再帰的に呼び出す場合、各呼び出しは独自のローカル変数を取得します。これがあなたの質問に対する答えであるかどうか、あなたの説明から判断するのは困難です。あなたは本当にいくつかのコードを投稿する必要があります。

于 2012-09-23T22:03:57.930 に答える