この問題はしばらく前から私に起こりました。ネストされたfor
ループをPythonで書く簡単な方法はありますか?たとえば、私のコードが次のようになった場合:
for y in range(3):
for x in range(3):
do_something()
for y1 in range(3):
for x1 in range(3):
do_something_else()
これを行う簡単な方法はありますか?私はこのコードが機能することを知っていますが、私のように2つのスペースを使用する代わりにインデントすると、問題になる可能性があります。
例では、物事を簡単にするために、ネストされたfor
ループは4つしかありませんでした。
例のようにデカルト積を頻繁に反復している場合は、 Python 2.6のitertools.product -を調査するか、以前のPythonを使用している場合は独自に作成することをお勧めします。
from itertools import product
for y, x in product(range(3), repeat=2):
do_something()
for y1, x1 in product(range(3), repeat=2):
do_something_else()
これは、多次元空間をループする場合にかなり一般的です。私の解決策は:
xy_grid = [(x, y) for x in range(3) for y in range(3)]
for x, y in xy_grid:
# do something
for x1, y1 in xy_grid:
# do something else
この種のプログラムロジックに直面したとき、ループのシーケンスを2つ以上の個別の関数に分割するでしょう。
Python=のもう1つのテクニックは、ループの代わりに list comprehensions を使用することです。
各ループに何らかの独立した意味があると仮定して、それらを名前付き関数に分割します。
def do_tigers():
for x in range(3):
print something
def do_lions():
do_lionesses()
for x in range(3):
do_tigers()
def do_penguins():
for x in range(3):
do_lions()
..etc.
もっと良い名前を選んだのかもしれません。 8-)
技術的には、itertools.product
Nシーケンスのデカルト積を取得し、それを繰り返します。
for y, x, y1, x1 in itertools.product(range(3), repeat=4):
do_something_else()
しかし、それが実際に読みやすさの点であなたに勝つとは思いません。
Pythonイテレータ、特にジェネレータは、複雑なループのニースリファクタリングを可能にするために正確に存在します。もちろん、単純な例から抽象化を引き出すことは困難ですが、_3
_はパラメーターである必要があると想定して(おそらくrange(3)
全体を指定する必要がありますか?)、2つの関数呼び出しには、ループ変数であるいくつかのパラメーターが必要です。コードをリファクタリングできます。
_ for y in range(3):
for x in range(3):
do_something(x, y)
for y1 in range(3):
for x1 in range(3):
do_something_else(x, y, x1, y1)
_
例:
_def nestloop(n, *funcs):
head = funcs[0]
tail = funcs[1:]
for y in range(n):
for x in range(n):
yield head, x, y
if tail:
for subtup in nestloop(n, *tail):
yield subtup[:1] + (x, y) + subtup[1:]
for funcandargs in nestloop(3, do_something, do_something_else):
funcandargs[0](*funcandargs[1:])
_
正確な種類のリファクタリングは、正確な目的のために微調整する必要があることは間違いありませんが、イテレータ(通常、実際には単純なジェネレータのみ)が非常に優れたループのリファクタリングを提供するという一般的な点は残ります-すべてのループロジックはジェネレータの内部に入ります。また、アプリケーションレベルのコードには、単純なfor
ループと、forループで生成されたアイテムの実際のアプリケーション関連の処理が残されています。
私の個人的な議論は、6つのネストされたループがある場合、おそらく何かが間違っているということでしょう...
そうは言っても、機能分解はあなたが探しているものです。一部のループが個別の関数呼び出しで発生するようにリファクタリングしてから、それらの関数を呼び出します。
コードから、xとyが0..2の範囲にあるすべての可能なポイントのペアで操作を実行したいようです。
それを行うには:
for x1,y1,x2,y2 in itertools.product(range(3), repeat=4):
do_something_with_two_points(x1,y1,2,y2)
操作 do_something_with_two_points
は81回呼び出されます-ポイントのすべての可能な組み合わせに対して1回。
その方法は、かなり簡単で簡単に見えます。ループの複数の層に一般化したいということですか...実際の例を挙げられますか?
私が考えることができるもう1つのオプションは、関数を使用してパラメーターを生成し、それをループに適用することです
def generate_params(n):
return itertools.product(range(n), range(n))
for x,y in generate_params(3):
do_something()