サンプル・プログラム
複利計算
10,000円のお金を金利1.1%で10年間預けると‥‥この複利計算をExcelで表計算すると左図のようになる。
10年ならいいが、これが30年、50年‥‥となると、大きな表計算になってしまう。
こういうときにプログラムを使うと省力化できる。
10年ならいいが、これが30年、50年‥‥となると、大きな表計算になってしまう。
こういうときにプログラムを使うと省力化できる。
calcCompoundInterest1.py
def calcCompoundInterest1(deposit, interest, period):
"""複利計算(再帰呼び出し版)
Args:
deposit(int): 現在の預金額
interest(float): 年間利率
period(int): 期間(年)
Returns:
ここでは for文 に range関数を組み合わせて使う。
for 変数 in range(初期値, 最終値 + 1):range関数により、初期値から最終値まで1ずつ増える整数の数列(リスト)が生成される。for文は、このリストから1つずつ値を取り出し、変数に格納し、インデントになっているブロック(処理1, 処理2‥‥)を実行する。
処理1;
処理2;
‥‥
月の大小を判定する(match文)
その年の1月1日から数えて、今日が何日目に当たるか――通日を求めるプログラムを作ってみよう。
daysOfYear.py
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
daysOfYear.py
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 する。
ここでは、for文 の中で、指定した年月の月の日数を求めるユーザー関数 dayOfMonth の結果を加算していくことで通日を計算するが、もし、dayOfMonth がエラーを返したら、if文を使って return する。
再び複利計算
話は複利計算に戻る。
お気づきの方も多いと思うが、Excelの FV関数を知っていれば、1行で複利計算ができる。
お気づきの方も多いと思うが、Excelの FV関数を知っていれば、1行で複利計算ができる。
ここで、10,000円のお金を金利1.1%で預けたとき、11,000円を超えるのは何年目か――この問題をExcelで解くにはやや手間がかかる。だが、プログラムであれば、前述の for文 に1行 if文 を追加するだけで解くことができる。
制御文を適切に使うことで、解決すべき課題が変わったときでも、少しの手間を加えるだけで対応可能になるのがプログラム化することのメリットだ。
制御文を適切に使うことで、解決すべき課題が変わったときでも、少しの手間を加えるだけで対応可能になるのがプログラム化することのメリットだ。
calcCompoundInterest2.py
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文 で複利計算中をしている最中、預金額 deposit が目標値 border を超えたら break文 を実行する。
break文 を実行すると繰り返し処理を中断し、ブロックの外へ制御を移す。つまり、if文を使って預金額 deposit が目標値 border を超えたら、複利計算を中断(ループから脱出)する。
for文を入れ子にする
Python では制御文の中に制御文を入れ子にすることができるから、for文の入れ子も可能である。これを利用し、掛け算九九表をつくることができる。
multiplicationTable.py
# 被乗数 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文は条件式によって繰り返すかどうかを制御するときに用い、複利計算はもちろん、約数やニュートン法(近似計算法)によって平方根を求めることができる。
while文は条件式によって繰り返すかどうかを制御するときに用い、複利計算はもちろん、約数やニュートン法(近似計算法)によって平方根を求めることができる。
コラム:GOTO文
BASICやFORTRANといったプログラミング言語では、break に相当する命令がなく、GOTO を使ってforループから脱出した。
"calcCompoundInterest2.py" と同じプログラムをBASICで書くと、次のようになる。
"calcCompoundInterest2.py" と同じプログラムをBASICで書くと、次のようになる。
GOTO は break と違って、ブロックからの脱出といった使い方の制約がなく、どこへでもジャンプすることができる。無制限に GOTO を使うとプログラムの流れを追うことが難しくなり、バグの温床となった。「コラム:スパゲッティプログラム - 4.1 if文」で紹介したスパゲッティ・プログラムの一種だ。
deposit = 10000
For i = 0 to 30
deposit = deposit * 1.011
If deposit > 11000 Then Goto *Disp
next
*Disp
Print deposit
End
こうして「GOTOは使ってはいけない」というお作法が広まったのだが、本来の経緯から言えば、ループからの脱出に用いるなど使用法を制限すればいいのであって、GOTO そのものが悪いわけではない。
さらに言えば、CPUに直接命令を書くことができるアセンブラ言語では、制御文に当たる命令がなく、ほとんどが条件演算とGOTOに相当する jmp命令(プログラムカウンタに代入する)の組み合わせで実現している。つまり、JavaScriptもBASICも、最終的にはGOTOに相当する命令をCPUに送っている。
本シリーズを含め、プログラミングの参考書で「お作法」を見かけたときは、なぜそのお作法ができたのか、経緯まで遡って調べてほしい。そういう手間暇を惜しまないのが、技術者のあるべき姿なので。
コラム:for文はなぜ"for"なのか
for文はなぜ "for" なのだろうか――こんな疑問をもってロングマン英英辞典を引いてみると――
最初期のプログラミング言語である FORTRANの場合、for文 というものはなく、DO文 が繰り返し処理を担当している。たとえば FORTRAN 77 を使って書くと、次のような書き方になる。
当時のFORTRANは、繰り返し処理を入れ子にすることができないという弱点があった。こうした問題を解決すべくヨーロッパで開発されたのが ALGOL で、ここで初めて FOR文 という単語が登場する。
ALGOL はスイスのチューリッヒ工科大学で開発が始まった。英語版ALGOLでは FOR文 であるが、当初はドイツ語の FÜR文だった。そして、ドイツ語の für の意味を wiktionary で調べてみると、前置詞として「6.(時間)..間」という意味があった。for文はドイツ語由来だったのだ。
なお、DO文 については、"do~while" という形の繰り返し処理ができるプログラミング言語がまだいくつか残っている。
1 used to say who is intended to get or use something, or where something is intended to be used――やはり繰り返しの意味はない。
2 in order to help someone or something
3 used to say what the purpose of an object, action etc is for doing something
最初期のプログラミング言語である FORTRANの場合、for文 というものはなく、DO文 が繰り返し処理を担当している。たとえば FORTRAN 77 を使って書くと、次のような書き方になる。
DEPOSIT = 10000.0つたない私の英語力でも、「ENDまでDOしなさい」と読めるので、繰り返しであることは一目瞭然である。これが、なぜ for になってしまったのか?
DO I = 0, 30
DEPOSIT = DEPOSIT * 1.011
END DO
当時のFORTRANは、繰り返し処理を入れ子にすることができないという弱点があった。こうした問題を解決すべくヨーロッパで開発されたのが ALGOL で、ここで初めて FOR文 という単語が登場する。
ALGOL はスイスのチューリッヒ工科大学で開発が始まった。英語版ALGOLでは FOR文 であるが、当初はドイツ語の FÜR文だった。そして、ドイツ語の für の意味を wiktionary で調べてみると、前置詞として「6.(時間)..間」という意味があった。for文はドイツ語由来だったのだ。
なお、DO文 については、"do~while" という形の繰り返し処理ができるプログラミング言語がまだいくつか残っている。
(この項おわり)
さらに、for文の中に if文などの制御文を組み込んだり、break文 を使って脱出することで応用範囲が広がる。