4.3 for文

(1/1)
ペンローズの階段
ペンローズの階段
複利計算や通日計算のように、繰り返し計算を行うときに for文が活躍する。
さらに、for文の中に if文などの制御文を組み込んだり、break文 を使って脱出することで応用範囲が広がる。

目次

サンプル・プログラム

複利計算

Excelで複利計算
10,000円のお金を金利1.1%で10年間預けると‥‥この複利計算をExcelで表計算すると左図のようになる。
10年ならいいが、これが30年、50年‥‥となると、大きな表計算になってしまう。
こういうときにプログラムを使うと省力化できる。
def calcCompoundInterest1(deposit, interest, period):
	"""複利計算(再帰呼び出し版)
	
	Args:
		deposit(int):		現在の預金額
		interest(float):	年間利率
		period(int):		期間(年)
	
	Returns:
Pythonで複利計算
for文 - 2.2 繰り返し処理」で紹介した for文を使うと、期間(年数)が何百年になろうが、実質3行のプログラムで複利計算ができる。
ここでは for文range関数を組み合わせて使う。
for 変数 in range(初期値, 最終値 + 1):
    処理1;
    処理2;
    ‥‥
range関数により、初期値から最終値まで1ずつ増える整数の数列(リスト)が生成される。for文は、このリストから1つずつ値を取り出し、変数に格納し、インデントになっているブロック(処理1, 処理2‥‥)を実行する。

月の大小を判定する(match文)

その年の1月1日から数えて、今日が何日目に当たるか――通日を求めるプログラムを作ってみよう。
def dayOfMonth(year, month):
	"""月の日数を求める.

	Args:
		year(int):	西暦年
		month(int):	月
	
	Returns:
		bool True成功 / False 引数エラー, int: 月の日数
	
	"""
	match month:
		case 1:
			return True, 31
		case 2:
			if isLeapYear(year):
				return True, 29
			else:
				return True, 28
		case 3:
			return True, 31
		case 4:
			return True, 30
		case 5:
			return True, 31
		case 6:
			return True, 30
		case 7:
			return True, 31
		case 8:
			return True, 31
		case 9:
			return True, 30
		case 10:
			return True, 31
		case 11:
			return True, 30
		case 12:
			return True, 31
		case _:
			return False, 0
def dayOfYear(year, month, day):
	"""年初からの通日を求める.

	Args:
		year(int):	西暦年
		month(int):	月
		day(int):	日
	
	Returns:
		bool True成功 / False 引数エラー, int: 年初からの通日
	
	"""
	dayYear = 0
	for mm in range(1, month):
		(res, day2) = dayOfMonth(year, mm)
		if (res == False):
			return False, 0
		dayYear += day2
	dayYear += day

	return True, dayYear
if文の入れ子のときに見たように、一般的に、制御文のブロックの中に他の制御文を含めることができる。
ここでは、for文 の中で、指定した年月の月の日数を求めるユーザー関数 dayOfMonth の結果を加算していくことで通日を計算するが、もし、dayOfMonth がエラーを返したら、if文を使って return する。

再び複利計算

話は複利計算に戻る。
お気づきの方も多いと思うが、Excelの FV関数を知っていれば、1行で複利計算ができる。
ここで、10,000円のお金を金利1.1%で預けたとき、11,000円を超えるのは何年目か――この問題をExcelで解くにはやや手間がかかる。だが、プログラムであれば、前述の for文 に1行 if文 を追加するだけで解くことができる。
制御文を適切に使うことで、解決すべき課題が変わったときでも、少しの手間を加えるだけで対応可能になるのがプログラム化することのメリットだ。
def calcCompoundInterest2(deposit, interest, period, target):
	"""複利計算(目標金額になったら計算終了)
	
	Args:
		deposit(int):		現在の預金額
		interest(float):	年間利率
		period(int):		期間(年)
		target(int):		目標金額
	
	Returns:
プログラム "calcCompoundInterest2.py" では、目標値となる1,100円を変数 border に入力する。
for文 で複利計算中をしている最中、預金額 deposit が目標値 border を超えたら break文 を実行する。
break文 を実行すると繰り返し処理を中断し、ブロックの外へ制御を移す。つまり、if文を使って預金額 deposit が目標値 border を超えたら、複利計算を中断(ループから脱出)する。

for文を入れ子にする

Python では制御文の中に制御文を入れ子にすることができるから、for文の入れ子も可能である。これを利用し、掛け算九九表をつくることができる。
# 被乗数 1~9
for i in range(1, 10):
	str = ""	# 1行分の文字列
	# 乗数 1~9
	for j in range(1, 10):
		s = j * i
		str += f"{s:2d} "
	print(str)	# 1行を表示
プログラム "multiplicationTable.py" は、九九表の縦方向の被乗数を変数 i に代入して for文で回し、その次に横方向の乗数を変数 j に代入して入れ子にした内側の for文で回すことで、2次元の九九表を表示することができる。表示がズレないように、「フォーマット文字列 - 3.8 文字列」を使って、乗算結果を2桁で揃える。

練習問題

次回予告

次回は、もう1つの繰り返し制御 while文を学ぶ。
while文は条件式によって繰り返すかどうかを制御するときに用い、複利計算はもちろん、約数やニュートン法(近似計算法)によって平方根を求めることができる。

コラム:GOTO文

柵をすり抜けて侵入する人のイラスト
BASICやFORTRANといったプログラミング言語では、break に相当する命令がなく、GOTO を使ってforループから脱出した。

"calcCompoundInterest2.py" と同じプログラムをBASICで書くと、次のようになる。

deposit = 10000
For i = 0 to 30
    deposit = deposit * 1.011
    If deposit > 11000 Then Goto *Disp
next
*Disp
Print deposit
End
GOTObreak と違って、ブロックからの脱出といった使い方の制約がなく、どこへでもジャンプすることができる。無制限に GOTO を使うとプログラムの流れを追うことが難しくなり、バグの温床となった。「コラム:スパゲッティプログラム - 4.1 if文」で紹介したスパゲッティ・プログラムの一種だ。
こうして「GOTOは使ってはいけない」というお作法が広まったのだが、本来の経緯から言えば、ループからの脱出に用いるなど使用法を制限すればいいのであって、GOTO そのものが悪いわけではない。

さらに言えば、CPUに直接命令を書くことができるアセンブラ言語では、制御文に当たる命令がなく、ほとんどが条件演算とGOTOに相当する jmp命令(プログラムカウンタに代入する)の組み合わせで実現している。つまり、JavaScriptもBASICも、最終的にはGOTOに相当する命令をCPUに送っている。

本シリーズを含め、プログラミングの参考書で「お作法」を見かけたときは、なぜそのお作法ができたのか、経緯まで遡って調べてほしい。そういう手間暇を惜しまないのが、技術者のあるべき姿なので。
(この項おわり)
header