関数とは
今回はPythonの「関数」について学んでみましょう!
関数?数学の y = 2x + 5 みたいな?
数学かあ…
いやいや,数学の関数とはまた違うものですよ!
そうなんですね。
複雑なプログラムになってくると,同じ処理が何度も出てきたり,どんどんプログラムが長くなってしまいます。
そういうときに関数が便利です。
関数は,何度も使うプログラムをひとまとめにして呼び出せる便利な機能です。
よくわからないなあ…
具体的にみたほうがわかりやすいので,例をみながらやっていきましょう!
○時間○分○秒というのを○秒に変換してくれる関数を作ってみましょう。
○時間○分○秒を○秒に変換する関数
- def get_time(hour, minute, second):
- second_result = hour * 3600 + minute * 60 + second
- return second_result
- result = get_time(2, 50, 17)
- print(result)
いちばん最初のdefって?
これは関数ですよと宣言しているキーワードです。
英語で関数宣言するのが一般的で,今回は時間の計算だから,関数の名前をget_timeとしました。
def get_time(hour, minute, second):
次の行はこの関数で行われる計算を表しています。
second_result = hour * 3600 + minute * 60 + second
2時間50分17秒の場合,
2(hour) × 3600 = 7200秒
50(minute) x 60 = 3000秒
17(second) = 17秒
すべてたして2時間50分17秒は10,217秒と計算できますね。
そして,この関数が使われたときにはこの計算結果を返してね,という意味で,3行目を,
return second_result
この定義した関数を使うためには,下のように呼び出します。
get_time(2, 50, 17)
1行目で定めた関数に数をあてはめてあげればいいですね。
プログラムに沿って解説をまとめますね
プログラムの解説
- # 「def」で関数であることを宣言。
- # get_time(●,▲,■)と書いたら下の計算をしてね,という意味
- def get_time(hour, minute, second):
- # get_time (●,▲,■)と書かれたら,●×3600+▲×60+■の計算をし結果を変数「second_result」に代入する
- second_result = hour * 3600 + minute * 60 + second
- # 最終的に「second_result」に代入された値を返す
- #ここまでが関数の定義
- return second_result
- # 関数「get_time」を使って計算した結果を変数「result」に代入する
- result = get_time(2, 50, 17)
- # resultに代入された値,つまり関数「get_time」の計算結果を表示
- print(result)
関数ができたら,あとはget_timeの()のなかの数値を変えるだけで,時間を秒に変換するプログラムができるってことね!
たしかに,いろいろな時間を秒に変換するたびにこのプログラムを書いてたんじゃ大変だから, 最初に数値が入力されたらこう計算する!って決めちゃうといいんだね。
そうです,もし関数を使わずに3つの時間を秒に変換し,表示するプログラムを書いたら,このようなプログラムになりますね。
毎回処理を書く例
- hour = 1
- minute = 34
- second = 55
- second_result = hour * 3600 + minute * 60 + second
- print(second_result)
- hour = 6
- minute = 40
- second = 34
- second_result = hour * 3600 + minute * 60 + second
- print(second_result)
- hour = 2
- minute = 50
- second = 17
- second_result = hour * 3600 + minute * 60 + second
- print(second_result)
関数を使うとこんなにすっきりします。
関数を使った例
- def get_time(hour, minute, second):
- second_result = hour * 3600 + minute * 60 + second
- return second_result
- result = get_time(1, 34, 55)
- print(result)
- result = get_time(6, 40, 34)
- print(result)
- result = get_time(2, 50, 17)
- print(result)
関数は何か情報さえ渡せば勝手に処理してくれるロボットを作るイメージですね。
このときの2と5を引数(ひきすう),7を戻り値といいます。
関数が引っ張ってくる数字だから引数,関数から戻す値だから戻り値ですね。
引数や戻り値がないパターンもあるからイメージしてみましょう。
例えば,さっきの計算結果が10000秒以上なら「タイムオーバー!」それ以外は「クリア!」と表示する処理を実現する関数をつくってみましょう。
戻り値の無い関数の例
- def check_time(hour, minute, second):
- second_result = hour * 3600 + minute * 60 + second
- if second_result >= 10000:
- print("タイムオーバー!")
- else:
- print("クリア!")
- check_time(2, 50, 17)
計算結果から数値を返すんじゃなく,「タイムオーバー!」か「クリア!」を表示するだけだから,戻り値(return)が無くていいんですね。
そうですね。今回は,計算結果を引数として入れて,その結果からプリントするだけの処理をするだけの関数ですね。
イメージはこんな感じです。
戻り値(return)の無い関数はわかりましたが,引数の無い関数もあるんですか?
ありますよ。
たとえば,次のようなプログラムです
引数の無い関数の例
- def display_clear():
- # display_clear()という関数が呼び出されたら,「クリア!」と表示する
- print('クリア!')
- sum = 6 + 5
- # sumが10以上の数だったら, display_clearの関数を呼び出す(つまり「クリア!」と表示する)
- if(sum >= 10):
- display_clear()
イメージにするとこんな感じでしょうか。
これだったら,わざわざ関数にする必要なくない?
余計にめんどうになった感じがする…
この例だと特に関数にする必要もないと思うかもしれないですね。ただ,あえてクリアしたときの表示を関数にすることで,あとから「クリア!」ではなく「勝ち!」に変えたいと思ったときに,変更箇所がこの関数だけになって便利なことがあります。
複雑なプログラムをつくるときは,どの機能のまとまりを関数にするかを考えることが,よいプログラムをつくるためには必要です。
なるほど。まだ実感はないけど,関数の大切さはわかりました。
それでは演習してみましょう。 次のプログラムは三角形の面積を求めたプログラムです。関数を用いた書き方にしてください。
演習1
- 次のプログラムは3つの三角形の面積を求めたプログラムです。関数を用いた書き方にしましょう。
- teihen = 2
- takasa = 4
- menseki = teihen * takasa / 2
- print(menseki, "平方センチメートル")
- teihen = 3
- takasa = 8
- menseki = teihen * takasa / 2
- print(menseki, "平方センチメートル")
- teihen = 6
- takasa = 5
- menseki = teihen * takasa / 2
- print(menseki, "平方センチメートル")
- # 関数名は「triangle_area」としています。別の名前でも問題ありません。
- def triangle_area(teihen, takasa):
- area = teihen * takasa / 2
- return area
- menseki_1 = triangle_area(2, 4)
- print(menseki_1, "平方センチメートル")
- menseki_2 = triangle_area(3, 8)
- print(menseki_2, "平方センチメートル")
- menseki_3 = triangle_area(6, 5)
- print(menseki_3, "平方センチメートル")
次の章では,スーパーのレジの機能を関数で実現してみましょう!
実習:レジ機能を作ろう
関数を利用して,レジ機能をつくって商品情報の入力,合計金額の表示,レシートの表示をするプログラムをつくってみましょう!!
レジには,どういう機能があると思いますか?
まずはフローチャートでレジの機能を整理してみましょう。
商品のバーコードを何度か読み取って,計算して,合計金額出して,おつり計算して・・・
こんな感じでしょうか?
そうそう,それでは順にプログラムを組み立てていきましょう。
このページの右下にある「プログラム」ボタンを押し,「レジエディタ」を選択しましょう。
今回はこの専用エディタを使って,疑似的なレジ機能をプログラムしていきます。
プログラムが完成するとこんな風に動くようになりますよ。
まずは商品の名前,個数,金額,税率の情報を読み取る動きからつくっていきますが,すべて自分でプログラムを書いて設計するのはなかなか難しい内容です。 しっかり読んで,プログラムの構造を理解することに注力してください。
レジ機能をつくるのに必要だけれども,今回の講座では扱わない部分については,最初からエディタに入っています。消さないようにしてください。
購入する商品をリストに登録する関数
- def add(name, num, price, tax):
- print('商品名: ' + name)
- print('個数: ' + str(num))
- print('金額: ' + str(price))
- print('税率: ' + str(tax))
- # データをリストにしてitemsに追加していく
- items.append([name, num, price, tax])
説明しますね。
読み取る情報をそれぞれ,name(名前), num(個数), price(値段), tax(税率)とし,
バーコードを読み取ったらその情報をitems
と名付けたリストに追加していくというプログラムですね。
このプログラムをエディタにコピーしましょう。
プログラムのコピーができたら,レジに購入する商品を追加しましょう。
今回バーコードを読み取る仕組みはないので,読み取ったつもりで情報をエディタに入力していきます。
次のように名前,個数,値段,税率を入力して「追加」を押すと,add関数が呼び出されて,読み取ったつもりの商品がリストに追加されます。「追加」を押して,正しくプログラムが動いているかどうか,確認しましょう。
あれ…僕が書いたプログラムだとこんなエラーが出てきたんだけど…
Traceback…? NameError…?
プログラムに問題があれば,それに合わせたエラーメッセージが表示されます。
下の四角で囲んだ部分のプログラムをみてください。
今回の場合は,インデントを入れていないので,関数の外に記述されたものという扱いになっているため,エラーが出ているんですよ。
プログラムを書くときにはインデントに気をつけなきゃいけないんだったね!
「一覧表示」をクリックすることで今入力されている商品をみることができます。また「リセット」をクリックすることで,入力したものを消すこともできます。
次に,合計値を求める関数を作ります。
購入する商品の合計金額を計算する関数
- def sum():
- print('sum')
- total = 0
- for item in items:
- num = item[1]
- price = item[2]
- tax = item[3]
- total = total + int(price * num * (1 + tax * 0.01))
- print(total)
- return total
sumが合計を求める関数ってことですね。
items
に追加されたデータから,num
,price
,tax
を取り出して計算しているってことか。
途中のint(price * num * (1 + tax * 0.01))
ってなんですか?
消費税の計算をすると小数点になる場合もありますよね。
その場合は切り捨てにするので, int()
という関数で整数に変換しています。
「合計表示」を押すと,sum関数が呼び出されて,合計金額が表示されます。
正しく計算されるか確認してみましょう。
合計が表示されました!
次は支払いからおつりを計算する関数です。
支払いからおつりを計算する関数(amount: 支払い,戻り値: おつり)
- def pay(amount):
- print('pay: ' + str(amount))
- change = amount - sum()
- if(change < 0):
- print("代金がたりません")
- print("おつり" + str(change) + "円")
- return change
「支払い」を押すと,pay関数が呼び出されて,おつりの計算がされます。
預かり金額をエディタに入力して「支払い」を押し,正しくおつりが表示されるか確認しましょう。
次はレシートを表示するための関数です。
レシートを表示する関数
- def show_receipt():
- print('show_receipt:' + str(amount))
- for item in items:
- name = item[0]
- num = item[1]
- price = item[2]
- tax = item[3]
- print(name, num, price, "円", tax)
「レシート表示」を押すと,show_receipt関数が呼び出されて,レシートが表示されます。「追加」した商品や金額がすべて表示されることを確認しましょう。
ここでは簡単な商品一覧を表示する機能になっています。次の応用編で,もっとレシートっぽくなります。
どうでしょうか?レジの機能は実現できましたか?
できました!
レジ機能:レシート表示(チャレンジ問題)
応用課題に取り組みたい人は,レシートをもっと詳しく再現してみたり,現金払いのときのおつりのお札や枚数の計算をするプログラムを組み込んだりしてみましょう!
レシートの例を貼っておきますね。
シンプルなレシートですね。
コンビニのレシートなど改めてみると,いろんな情報が記載されているのがわかって面白いですよ。
さて,レシートを表示するプログラムの例を紹介します。
レシート表示(amount: 支払い金額)
- def show_receipt(amount):
- print("[領収書]")
- print("夕陽ケ丘ストア")
- total_price = 0
- count = 0
- total_tax = 0
- # 商品毎に繰り返し
- for item in items:
- name = item[0]
- num = item[1]
- price = item[2]
- tax = item[3]
- total_price = total_price + price * num
- count = count + num
- total_tax = total_tax + price * num * tax * 0.01
- print(name)
- print(" ¥" + str(price), str(num)+"点", "¥" + str(price * num))
- print("-------")
- print("小計 ", str(count) + "点", "¥" + str(total_price))
- print("外税計 ", " ¥" + str(int(total_tax)))
- print("合計 ", " ¥" + str(int(total_price + total_tax)))
- print("お預かり", " ¥" + str(int(amount)))
- print("おつり ", " ¥" + str(int(amount - total_price - total_tax)))
できました!
でも,複雑になってきましたね。
レジ機能:おつり(チャレンジ問題)
次は,おつりを応用していきましょう。
え,さっきおつりの金額をだしましたよ?
最近のレジは,おつりを自動で出してくれるところもありますよね?
支払い関数を千円札が何枚,500円玉が何枚と計算できる機能に進化させてみましょう。
そっか,たしかにおつりを自動で出すためにもプログラムで計算しないと!
改めて考えると,そういう便利なことも誰かがプログラミングしてくれているんですね。
ここでは関数というより,アルゴリズムを考える方が大変かもしれないですね。
どういうロジックで処理をするとそれぞれのお金の枚数を計算できるか考えてみてください。
どうやって考えればいいんだろう…
普通は一番大きな10000円札はおつりにはならないですね。
5000円札は,おつりが5000円以上のときに1枚使いますね。
その後,1000円札が何枚返すべきか計算して,
その後,500円玉の枚数,と続いていくのかしら。
例えば3422円のおつりの場合,どう考えますか?
1000円札を3枚返すと,残りは422円。
あとは100円玉を4枚返して,残りは22円。
10円玉を2枚と1円玉を2枚ですね。
そうそう,その調子ですよ。
大きいお札から計算していけば良さそうですね。
わかった!
大きなお札からわり算して,そのあまりを次に大きなお札でわり算してを繰り返していけばできそう!
そうですね。実際のプログラムは次のようになります。
支払いとおつり表示
- # 支払い
- def pay(amount):
- change = amount - sum()
- # 預り金を保存
- if(change < 0):
- print("代金がたりません")
- # おつりの枚数を表示
- show_change(amount - sum())
- return change
- # おつり表示
- def show_change(change):
- print(change)
- change_left = change
- kinds = [10000, 5000, 1000, 500, 100, 50, 10, 5, 1]
- for kind in kinds:
- count = change_left // kind # わり算の商
- change_left = change_left % kind # わり算の余り
- if(kind >= 1000):
- print(str(kind) + "円札:" + str(count) + "枚")
- else:
- print(str(kind) + "円玉:" + str(count) + "枚")
わあできた!
なるほど,リストに10000円札から1円玉までの数字をいれて,
繰り返しでわり算しているんですね。
わり算も「商」と「あまり」を使っているのがわかりました!
だいぶ複雑になってきましたね。でも実際は小学校で習う算数の計算を使っているだけなので,ゆっくり確認してみてください。
次はさらに応用としてプログラミングで計算する演習にチャレンジしてみましょう!
関数を使いこなそう:再帰関数
関数は,数学の問題を解くときにも使われることが多いですよ。
計算するとき?電卓とは違うのかな?
そういえば,円周率はプログラムを組んで計算しているとか聞いたことがあります。
そうですね。円周率は小数点以下50兆桁まで計算されているそうですよ。皆さんがこの教材をみている間にさらに更新されているかもしれませんね。
このように,電卓を使っても人間がやりきれないような計算もプログラミングで解くことができます。
たしかに!それは人手ではできない。
手はじめに,1〜1000の数字をたしていくプログラムをつくってみましょう!
1+2+3+....+1000ってことですね。
はいそうです。
プログラムは次のようになります。
nまでの自然数の和を計算する関数
- def ruikei(n):
- total = 0
- for i in range(1, n + 1):
- total = total + i
- return total
- print(ruikei(1000))
ruikeiという関数が,引数に入れた数字までの和を繰り返しを使って計算していますね。
そうです。そして,次のように書くこともできます。
(ブラウザの制限でnを大きくしすぎるとエラーになります。反応がなくなってしまったらブラウザを再起動してください。)
- # nまでの自然数の和を計算する関数(再帰)
- def ruikei(n):
- if(n < 1):
- return 0
- return n + ruikei(n - 1)
- print(ruikei(1000))
あれ?関数のなかで関数を呼んでる?
さっきのプログラムよりは少し短く書けてますね。
はい。ちょっと難しいですが,落ち着いて1つずつ考えてみましょう。
ruikei(n) → n + ruikei(n - 1)
ruikei(n - 1) → n - 1 + ruikei(n - 2)
ruikei(n - 2) → n - 2 + ruikei(n - 3)
と続きます。
n = 3の場合
ruikei(3) → 3 + ruikei(2)
ruikei(2) → 2 + ruikei(1)
ruikei(1) → 1 + ruikei(0)
ruikei(0) → 0
引数が1つずつ減った関数がどんどん呼ばれていくってことですね?
はい。そうなります。図解すると以下のようになります。
なるほど!
それでは,次は自然数nを引数に,1〜nのすべての積を計算する関数をつくってみましょう!
nの階乗ですね。
階乗を求める関数
- def kaijo(n):
- if(n <= 1):
- return 1
- return n * kaijo(n - 1)
- print(kaijo(10))
なるほどできました。
この関数には名前がついていて,「再帰関数」といいます。難しかった人やさらに詳しく知りたい人は調べてみてくださいね。
こんな風に,計算機でも面倒な計算や,複雑な計算式をコンピュータで実現すると便利になる場合もあります。
今回の合計を求める関数は等差数列の和の公式を使うともっと計算を簡単にすることもできます。
プログラミングアルゴリズムを考えていくのも楽しいですよ。