for
や while
を利用すると繰り返しの処理(ループ処理)が可能になり,2重ループや3重ループも記述できます.しかしながら,非常に深いループや,深さが異なるループが必要になることがあります.このような場合には再帰関数を利用すると良いでしょう.
まず,指定した値(例えば 1)から10までの値を順番に表示するプログラムを for
を使って書いてみます.
for i in range(1,11):
print(i, end=" ")
1 2 3 4 5 6 7 8 9 10
ほぼ同じ処理を再帰関数を使って記述すると次のようになります.
def loop(i):
if (i > 10):
return
print(i, end=" ")
loop(i+1)
loop(1)
1 2 3 4 5 6 7 8 9 10
上のプログラムでは,最初に6行目にある通り,引数 1 が与えられて関数 loop
が実行されます.4行目で 「1 」が表示され,5行目では引数を 2 として関数 loop
(自分自身)を呼び出します.そこでは「2 」が表示され,また引数を 3 として自分自身を呼び出す,ということが繰り返されます.引数が 10 を超えた時に3行目の return
が実行され,これまで呼び出されていた関数の処理が順次終了して,すべての処理を終えることになります.
逆順で表示したい場合には次のように書くこともできます.
def loop_r(i):
if (i == 0):
return
print(i, end=" ")
loop_r(i-1)
loop_r(10)
10 9 8 7 6 5 4 3 2 1
ここで説明した通り,for
を重ねると2重ループが実現でき,例えば次のように九九の表を出力することができます.
for i in range(1,10):
for j in range(1, 10):
seki = f"{i * j:4d}"
print(seki, end="")
print("")
1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 3 6 9 12 15 18 21 24 27 4 8 12 16 20 24 28 32 36 5 10 15 20 25 30 35 40 45 6 12 18 24 30 36 42 48 54 7 14 21 28 35 42 49 56 63 8 16 24 32 40 48 56 64 72 9 18 27 36 45 54 63 72 81
繰り返し for
を使うと,辞書(やリスト)に格納された値を順に取り出すことができます.まず,2名の学生(田中さんと佐藤さん)についての英語と数学の得点を格納した辞書を準備します.
scores = {
'Tanaka': {'English': 50, 'Mathematics': 70},
'Sato': {'English': 60, 'Mathematics': 60}
}
scores
{'Tanaka': {'English': 50, 'Mathematics': 70}, 'Sato': {'English': 60, 'Mathematics': 60}}
次のコードでは一人ずつデータを取り出すことができます.
for name, values in scores.items():
print(name, values)
Tanaka {'English': 50, 'Mathematics': 70} Sato {'English': 60, 'Mathematics': 60}
2重ループで記述すると,得点だけを順番に取り出すことができます.
for name, values in scores.items():
for subject, score in values.items():
print(score, end=" ")
50 70 60 60
さらに取り出した得点をリスト raw_scores
に格納してみます.なお,append
はリストの最後に要素を追加するメソッドです.
raw_scores = []
for name, values in scores.items():
for subject, score in values.items():
raw_scores.append(score)
raw_scores
[50, 70, 60, 60]
辞書の深さが同じであれば,要素の個数が変わっても同じプログラムで動作します.例えば,田中さんだけ科学の得点もある辞書を準備します.
scores = {
'Tanaka': {'English': 50, 'Mathematics': 70, 'Science': 80},
'Sato': {'English': 60, 'Mathematics': 60}
}
scores
{'Tanaka': {'English': 50, 'Mathematics': 70, 'Science': 80}, 'Sato': {'English': 60, 'Mathematics': 60}}
これを2重ループで処理すると,すべての得点を取り出してリスト化することができました.
raw_scores = []
for name, values in scores.items():
for subject, score in values.items():
raw_scores.append(score)
raw_scores
[50, 70, 80, 60, 60]
非常に深いループを for
で記述することは面倒ですし,深さが異なる場合には for
では記述することができません.このような場合は再帰関数を使う必要があるでしょう.例えば,次のような複雑な構造の辞書を定義します.具体的には英語,数学,科学にはその直下に得点が与えられているものの,歴史は日本史と世界史に細分化されて得点が与えられています.一方で日本語は現代文と古典に細分化された上にさらにA,Bなどに細分化されています.
scores = {
'Tanaka': {
'English': 50,
'Mathematics': 70,
'Science': 80,
'History': {'Japanese History': 50, 'World History': 40}
},
'Sato': {
'English': 60,
'Mathematics': 60,
'Japanese': {
'Contemporary Japanese': {'A': 50, 'B': 50, 'C': 40},
'Classical Japanese': {'A': 60, 'B': 50}}
},
}
scores
{'Tanaka': {'English': 50, 'Mathematics': 70, 'Science': 80, 'History': {'Japanese History': 50, 'World History': 40}}, 'Sato': {'English': 60, 'Mathematics': 60, 'Japanese': {'Contemporary Japanese': {'A': 50, 'B': 50, 'C': 40}, 'Classical Japanese': {'A': 60, 'B': 50}}}}
このように深さが異なる複雑な辞書の処理を for
の繰り返しだけで美しく記述することはできないでしょう.例えば,2重ループだと歴史や国語の得点を正しく取得することができません.
for name, values in scores.items():
for subject, score in values.items():
print(score, end=" ")
50 70 80 {'Japanese History': 50, 'World History': 40} 60 60 {'Contemporary Japanese': {'A': 50, 'B': 50, 'C': 40}, 'Classical Japanese': {'A': 60, 'B': 50}}
深さが異なる状況では再帰を利用することで美しく繰り返しを実装することができます.なお,isinstance()
関数は引数に指定したオブジェクトの型をチェックする関数で,ここでは value
というオブジェクトが int
(整数型)であるかどうかをチェックしています.つまり,整数であればその値を表示し,そうでなければ(まだ辞書の形式であれば)自分自身を呼び出すという処理をしています.
def flatten_dictionary(dic):
for name, value in dic.items():
if isinstance(value, int):
print(value, end=" ")
else:
flatten_dictionary(value)
flatten_dictionary(scores)
50 70 80 50 40 60 60 50 50 40 60 50
上のように再帰をうまく使えば非常に深いループや深さの異なるループを記述することができるようになります.なお,得点を画面に表示するのではなくリストに格納するような場合は,次のように記述すると良いでしょう.
def flatten_dictionary(dic):
for name, value in dic.items():
if isinstance(value, int):
flatten_scores.append(value)
else:
flatten_dictionary(value)
return flatten_scores
flatten_scores = []
flatten_scores = flatten_dictionary(scores)
print(flatten_scores)
[50, 70, 80, 50, 40, 60, 60, 50, 50, 40, 60, 50]
このページでは辞書(つまり JSON)形式のデータで説明しましたが,XMLデータやXBRLデータであっても同じようにできるはずです.