I think ** multiple for loop (nesting) ** is one of the most avoidable processes in programming. About a month ago, the article "Why do we stubbornly avoid for" was buzzed, and 2020/11/3 With more than 1000 LGTMs now, many people don't want to use for loops too much. There are several techniques in Python that can easily avoid multiple for loops, so I've summarized the ones I use most often. If you have any other, please let us know in the comments.
I think there are various reasons for each person (also discussed in detail in the above article), but personally I have the following two points.
As an example of multiple for loops, let's write a program that searches all 0 to 999 by turning a for statement from 0 to 9 for each of the 3 digits, and searches for numbers that are 500 or more and doublet. (suitable) Even just a triple for loop will make you sick.
bad example
result = []
for i in range(10):
for j in range(10):
for k in range(10):
summed = i*100+j*10+k
if i == j == k and summed >= 500:
result.append(summed)
print(result)
#Execution result-> [555, 666, 777, 888, 999]
It's still good because the process is simple, but it gets really hard to read when it gets more complicated.
Often, when you meet certain conditions, you want to get out of all the loops, not just the innermost loop. As an example, write a program that ends the process if even one doublet is found in the process of 1. above. When I search for "how to get out of python multiple loops" on Google, although there is a difference that flag is not used, it is written as follows.
There is a bad example break
result = []
for i in range(10):
for j in range(10):
for k in range(10):
summed = i*100 + j*10 + k
if i == j == k and summed >= 500:
result.append(summed)
break
else:
continue
break
else:
continue
break
print(result)
#Execution result-> [555]
This is pretty hard to read. It's very hard to tell where and where the indentation is the same. If you try to write more complex processes, you'll end up with code that no one can understand.
By using itertools, you can generate iterators that produce all combinations and avoid multiple for loops. Also, since iterators are generated, even if you make all the combinations, it will not consume a lot of memory. If you want to list all the combinations you have created, just enclose the iterator created by the built-in function list (). (Be careful about memory consumption when listing)
When I write the code that breaks out of the loop using itertools,
When using itertools
import itertools
all_nums_iter = itertools.product(range(10),repeat=3)
result = []
for num in all_nums_iter:
summed = num[0]*100 + num[1]*10 + num[2]
if num[0] == num[1] and num[1] == num[2] and summed >= 500:
result.append(summed)
break
print(result)
#Execution result-> [555]
I think it's easier to see. By the way, itertools has a function that can not only enumerate all, but also enumerate combinations and permutations with and without duplication.
If you're already using a numpy array, you can use numpy.npindex to avoid multiple for loops. numpy.npindex turns the for statement so that all the values in the received array are brute force. In the code below, options.shape is (10, 10, 10), so i, j, and k are full search for statements from 0 to 9 respectively.
numpy.When using npindex
import numpy as np
options = np.array([[[100*i+10*j+k for i in range(10)] for j in range(10)] for k in range(10)], dtype="int")
result = []
for i, j, k in np.ndindex(options.shape):
summed = options[i,j,k]
if i == j and j == k and summed >= 500:
result.append(summed)
break
print(result)
#Execution result-> [555]
It's pretty refreshing.
It's a little difficult and I think it's used in a limited number of situations, but you can also do a full search with a recursive function. If you are not familiar with recursive functions, see "What kind of world will expand if you learn recursive functions" in the reference below. I think it's pretty easy to understand.
When using a recursive function
def dfs(depth, num):
if depth == 3:
num_str = str(num).zfill(3)
return num if num_str[0] == num_str[1] == num_str[2] and num >= 500 else None
for i in range(10):
ret = dfs(depth + 1, num + 10**(2-depth) * i)
if ret:
return ret
print(dfs(0, 0))
#Execution result-> 555
You can use it in a limited number of situations, but you can write intuitive code in specific situations (DFS: depth-first search, etc.).
Why do we stubbornly avoid for Method often used in atcoder python version What kind of world will expand when you learn recursive functions
Thank you for reading to the end. Let's simplify the hard-to-read multiple for loops. If you know of any other easy way to write, please let me know in the comments. If you find this article helpful, please use LGTM!
Recommended Posts