タートルグラフィックスで学ぶ Python のキホン 第 14 章
教材コード
第 14 章

値を返す関数

計算した答えを,関数に持ち帰らせます.

前の章,関数を作れるようになりました.引数を使えば, 外から関数の中へ値を渡すこともできました.

では逆はどうでしょうか.関数に計算をしてもらって, その答えを受け取りたいことがあります. 「BMI を計算して」「この 2 点の距離を教えて」「次に塗る色を決めて」. こういうとき必要になるのが,今回の return(リターン)です.

return で返す値を戻り値(もどりち)といいます. 教科書によっては「返り値(かえりち)」「リターン値」とも呼びますが, どれも同じもので,英語の return value の訳が定まっていないだけです. この教材では戻り値と呼びます.

この章でできるようになること
  • return で,関数から値を持ち帰らせられる
  • 戻り値を変数に入れる・そのまま使う・print する,が使い分けられる
  • return を実行すると関数がそこで終わることがわかる
  • 戻り値のない関数が None を返していることがわかる
  • 座標・距離・色を返す関数を作って,タートルの絵に使える
  • math モジュールの関数が使える(度とラジアンの違いに気をつけられる)
  • 関数を組み合わせて,プログラム全体を見通しよく組み立てられる

14.1 関数の入力と出力

関数は,だと思ってください. 左から材料を入れると,右から製品が出てきます.

関数area(w, h)引数(入力)3 と 4戻り値(出力)12
図 14.1 関数は入力(引数)を受け取り,出力(戻り値)を返す箱.

第 13 章で作った関数は,入口だけがあって出口が無い箱でした. square(80) と呼ぶと絵が描かれますが, こちらに何かが返ってくるわけではありません. 今回はここに出口をつけます.

関数の種類呼び方
入力も出力もないdef hello():hello()
入力だけ(第 13 章)def square(size):square(80)
出力だけdef pi(): …… return 3.14x = pi()
入力も出力もある(今回の主役)def area(w, h): …… return w * hx = area(3, 4)
もとからある関数も同じ 4 種類 この 4 つの分け方は,自分で作る関数だけのものではありません. 第 13 章 13.6.4 節で見た組み込み関数も,同じように分けられます.
  • print("あ")入力だけ.画面に出すのが仕事で,こちらには何も返しません.
  • input()出力だけ.引数は要らず,打ち込まれた文字を返します.第 9 章で s = input() と書いたのは,まさに戻り値の受け取りでした.
  • max(3, 8)len("あいう")int("42")入力も出力もある関数です.
int(input()) という第 9 章からのお決まりの書き方も, 「input の戻り値を int の引数にする」という意味だったのです.

14.2 return — 値を持ち帰らせる

14.2.1 return の書き方

関数の中で return と書き,そのうしろに返したい値を書きます. それだけです.

area.py
# 横 w・縦 h の長方形の面積を返す
def area(w, h):
    answer = w * h
    return answer

s = area(3, 4)
print("面積は", s, "です")

print(area(10, 20))

コンソールに 面積は 12 です200 が出ます. area(3, 4) という書き方そのものが,12 という値に化ける と考えてください.

戻り値つき関数の形
def 関数の名前(仮引数):
    中身の命令
    return 返したい値

変数 = 関数の名前(実引数)
何を返すかはコメントに書く 「何を返す関数か」は return の行を見れば分かる,という考え方です. そのぶん,関数の上に「何を返す関数か」をコメントで書いておくと親切です.

14.2.2 戻り値の使い道は 4 つ

返ってきた値は「ただの数」です.数を書いてよい場所なら,どこにでも書けます.

use_return.py
# 2 倍にして返す
def double(x):
    return x * 2

# 1. 変数に入れる
a = double(5)
print(a)

# 2. そのまま print に渡す
print(double(5))

# 3. 計算の途中で使う
print(double(5) + double(3) + 1)

# 4. 別の関数(自分自身でもよい)の引数にする
print(double(double(5)))

10101720 と表示されます. 3 番目は 10 + 6 + 1,4 番目は double(10) ですね.

返したのに使っていない よくある失敗が「返したのに受け取っていない」です.
python
area(3, 4)          # 12 を返しているが,誰も受け取っていない
print("面積は 12 です")
エラーは出ませんが,計算した 12 は捨てられます. s = area(3, 4) のように変数に入れるか, print(area(3, 4)) のようにその場で使うか,必ずどちらかにしてください.
虫食い虫食い 14-A 三角形の面積

底辺 b・高さ h の三角形の面積を返す関数を作ります. ____ を埋めてください. 三角形の面積は「底辺 × 高さ ÷ 2」です.

fill13_area.py
# 底辺 b・高さ h の三角形の面積を返す
def triangle_area(b, h):
    ____ b * h / 2

s = triangle_area(10, 6)
print("面積は", ____, "です")

# 変数に入れずに,そのまま使ってもよい
print(____(8, 5))

14.2.3 return すると,関数はそこで終わる

return には,もうひとつ大事な働きがあります. return を実行した瞬間に,関数はそこで終わります. その先に命令が書いてあっても,実行されません.

return_exit.py
def test():
    print("ここは実行される")
    return 1
    print("ここは実行されない")

x = test()
print("戻ってきた値:", x)

これは欠点ではなく,むしろ便利な性質です. if と組み合わせると,こう書けます.

judge.py
# 点数から合否を返す
def judge(score):
    if score >= 60:
        return "合格"
    else:
        return "不合格"

print(judge(80))
print(judge(45))
print(judge(60))

return "合格" が実行されれば,そこで関数は終わるので, else を書かずに次のようにも書けます.どちらでも構いません.

else を省いた書き方
def judge(score):
    if score >= 60:
        return "合格"
    return "不合格"        # 60 点未満のときだけここに来る
返し忘れに注意 return がどの道を通っても実行されるかを確かめてください. 次の関数は score が 60 未満のとき, return に一度も出会わずに終わってしまいます.
python
def judge(score):
    if score >= 60:
        return "合格"
    # 60 未満のとき,何も返さない
エラーにはなりませんが,judge(30) の値は None(後述)になり, print すると None と表示されて「あれ?」となります.

14.2.4 返す値が無い関数と None

第 13 章で作った関数には return がありませんでした. では square(80) の「値」は何なのでしょうか.確かめてみましょう.

none.py
def hello():
    print("こんにちは")

x = hello()
print("hello() の値:", x)
print(type(x))

None(ノン)と表示されます. None は「値が無い」ということを表す特別な値です. return を書かなかった関数は, Python が自動的に None を返しているのです.

このことを知らないと,こんな失敗をします. 表示する関数を,さらに print で囲んでしまうという失敗です.

print_none.py
# 表示するだけの関数(return が無い)
def show_stars():
    print("*****")

print(show_stars())      # よくある勘違い

***** の次の行に,None が出てしまいます. まず show_stars() が実行されて ***** が表示され, そのあと,返ってきた None を外側の print が表示するからです. 直し方は簡単で,print を外して show_stars() と書くだけです.

print も None を返す print 自身も,画面に出すだけで何も返さない関数です. ですから print(print("あ")) と書くと, の次の行に None が出ます. 「None が 1 行よけいに出る」ときは, 戻り値の無い関数を print で囲んでいないかを疑ってください.
None は「値が無い」という値 Python では,何も返さない関数は None を返す というルールになっています. None は「値が無い」ことを表す特別な値で, 0""(空文字)とは別ものです.

14.2.5 戻り値はいくつまで?

引数はいくつでも書けました.戻り値はどうでしょうか. Python では,カンマで区切っていくつでも並べて返せます

min_max.py
# 小さいほう・大きいほうの順に並べて返す
def min_max(a, b):
    if a < b:
        return a, b
    else:
        return b, a

small, big = min_max(7, 3)
print("小さいほう:", small)
print("大きいほう:", big)

受け取る側も small, big = ... とカンマで並べます. 見た目は 2 つ返しているようですが,正確にいうと 「2 つの値をひとまとめにしたタプルという 1 個の値」を返しています. つまり「戻り値はひとつ」という原則は Python でも変わっていません. 書き方が楽になっているだけです.

ヒント この書き方は,座標のようにセットで意味を持つ値にぴったりです. 14.4 節で return x, y として使います.

14.3 いろいろな種類の値を返す

Python では型を書かないので,返せる値の種類に制限がありません. 数でも,文字でも,真偽値でも,同じ return で返せます. 「相性占い」を作りながら確かめましょう.

14.3.1 数を返す

aisho1.py
# 2 つの名前から相性(0〜99)を計算して返す
def aisho(name1, name2):
    total = 0
    for c in name1 + name2:
        total = total + ord(c)
    return total % 100

print("相性は", aisho("はなこ", "たろう"), "です")
print("相性は", aisho("因島", "花子"), "です")
ord と文字列の for for c in name1 + name2: は, 2 つの名前をつないだ文字列から1 文字ずつ取り出す繰り返しです. ord(c) はその文字に割り当てられている番号を返します (ord("A") は 65). 文字を数に変えて全部足し,% 100 で 0〜99 に収めているわけです. 占いですから,中身に深い意味はありません.

14.3.2 文字を返す

数のままでは味気ないので,判定したことばを返す関数にしてみます.

aisho2.py
def aisho(name1, name2):
    total = 0
    for c in name1 + name2:
        total = total + ord(c)
    return total % 100

# 点数からことばを返す
def judge(name1, name2):
    if aisho(name1, name2) > 70:
        return "ばっちり"
    elif aisho(name1, name2) > 40:
        return "そこそこ"
    else:
        return "努力しだい"

print("はなこさんとたろうさんの相性は", judge("はなこ", "たろう"))
print("因島さんと花子さんの相性は", judge("因島", "花子"))

judge の中で aisho を呼んでいることに注目してください. 戻り値のある関数どうしも,組み合わせられます

14.3.3 真偽値(True・False)を返す

第 5 章の if で使った score >= 60 のような式は, True(真)か False(偽)という値になるのでした. これも return で返せます.

aisho3.py
def aisho(name1, name2):
    total = 0
    for c in name1 + name2:
        total = total + ord(c)
    return total % 100

# 相性がよければ True,そうでなければ False を返す
def is_good(name1, name2):
    if aisho(name1, name2) > 60:
        return True
    else:
        return False

# 戻り値を if の条件にそのまま書ける
if is_good("はなこ", "たろう"):
    print("はなこさんとたろうさんは相性ばっちり!")
else:
    print("はなこさんとたろうさんは努力しだい...")

if is_good("因島", "花子"):
    print("因島さんと花子さんは相性ばっちり!")
else:
    print("因島さんと花子さんは努力しだい...")

if の条件には「最後に TrueFalse になるもの」を 書く決まりでした. ですから if is_good("はなこ", "たろう"): のように, 真偽値を返す関数をそのまま条件に書けます

is_ で始める 真偽値を返す関数には,is_(〜である)や has_(〜を持つ)で 始まる名前をつけるのが習慣です. is_goodis_even(偶数か)・is_hit(当たったか). こうしておくと if is_hit(x, y): が英語の文のように読めます.
== True は要らない is_good(...) は,中身がすでに TrueFalse です. if is_good(...) == True: と書く必要はありません (書いても動きますが,くどい書き方です). さらに,次のように書けば if すら要りません.
python
def is_good(name1, name2):
    return aisho(name1, name2) > 60
虫食い虫食い 14-B 偶数かどうかを返す関数

数が偶数なら True,奇数なら False を返す関数を作り, if の条件に使います. 偶数とは「2 で割った余りが 0」の数でした(第 7 章の剰余). ____ を埋めてください.

fill13_even.py
# n が偶数なら True,奇数なら False を返す
def is_even(n):
    if n % 2 == ____:
        return ____
    else:
        return ____

for i in range(6):
    if ____(i):
        print(i, "は偶数")
    else:
        print(i, "は奇数")

14.4 タートルで使う

戻り値は,絵を描くときにも大活躍します. 「どこに描くか」「どれだけ進むか」「何色にするか」を 計算して返す関数を用意しておくと,本文がとても短くなります.

14.4.1 座標を計算して返す関数

中心から半径 r,角度 deg の場所の座標を返す関数を作ります. 座標は x と y の 2 つなので,14.2.5 節で習った「並べて返す」を使います.

point_on_circle.py
from turtle import *
import math

# 中心から半径 r・角度 deg の位置の座標を返す
def point_on_circle(r, deg):
    x = r * math.cos(math.radians(deg))
    y = r * math.sin(math.radians(deg))
    return x, y

ht()

for i in range(12):
    x, y = point_on_circle(150, i * 30)
    pu()
    goto(x, y)
    pd()
    pencolor("royalblue")
    dot(18)

done()
図 14.2 時計の文字盤のように 12 個の点を打つ.

本文の側は x, y = point_on_circle(150, i * 30) の 1 行だけで, 「半径 150 の円周上の,30 度ずつの場所」を手に入れています. 三角関数の計算は関数の中に押し込めてあるので, 読むときに気にしなくてよくなりました. 面倒な計算を関数に隠すのも,関数の大事な働きです.

同じ point_on_circle を使って,点を線でつないだり, 半径を少しずつ変えたりすると,まったく違う絵になります. 変えたのは呼び方だけで,関数の中身は一文字も直していません.

図 14.3 150 度ずつの点を線でつなぐ
図 14.4 半径を少しずつ大きくしてらせんに

14.4.2 距離を返す関数

2 点の距離は,三平方の定理(ピタゴラスの定理)で求められます. 横の差と縦の差をそれぞれ 2 乗して足し,平方根をとります. Python では ** が「べき乗」, math.sqrt が「平方根」です.

line_length.py
from turtle import *
import math

# (x1, y1) と (x2, y2) の距離を返す
def line_length(x1, y1, x2, y2):
    yoko = x2 - x1
    tate = y2 - y1
    return math.sqrt(yoko ** 2 + tate ** 2)

ht()
pensize(3)
pencolor("royalblue")

pu()
goto(-150, -80)
pd()
goto(120, 110)

nagasa = line_length(-150, -80, 120, 110)
print("線の長さ:", nagasa)

pu()
goto(-60, 40)
pencolor("crimson")
write(round(nagasa, 1), font=("Arial", 22, "bold"))

done()
330.2
図 14.5 引いた線の長さを計算して,その場に書き込む.
round で丸める round(x, 1) は「小数第 1 位で四捨五入する」命令です. round(x) とだけ書くと整数に丸めます. 計算結果をそのまま表示すると 330.15148038438356 のように 長くなるので,表示のときは丸めましょう.

14.4.3 色を決めて返す関数

返すのは数だけではありません.色の名前(文字)を返す関数も作れます.

pick_color.py
from turtle import *

# i 番目に使う色を返す
def pick_color(i):
    if i % 3 == 0:
        return "crimson"
    elif i % 3 == 1:
        return "seagreen"
    else:
        return "royalblue"

pensize(3)
ht()

for i in range(12):
    pencolor(pick_color(i))
    fd(140)
    lt(150)

done()
図 14.6 pencolor(pick_color(i)) の 1 行で色が変わっていく.

pencolor(pick_color(i)) は, pick_color(i) が返した色の名前を, そのまま pencolor の引数にしています. 戻り値をほかの関数の引数にする,14.2.2 節の 4 番目の使い方です.

虫食い虫食い 14-C 大きさを決めて返す関数

i 番目の正方形の 1 辺の長さを返す関数を作り, だんだん小さくなる正方形を描きます. ____ を埋めてください.

fill13_size.py
from turtle import *

pensize(2)
ht()

# i 番目の正方形の 1 辺の長さを返す
def pick_size(i):
    ____ 160 - i * 15

# 1 辺 size の正方形を描く(第 13 章で作った関数)
def square(size):
    for j in range(4):
        fd(size)
        lt(90)

pu()
goto(-80, -80)
pd()

for i in range(8):
    square(____(i))
    lt(10)

done()

14.5 math を使った計算

math.sqrtmath.cos を,ここまで説明なしに使ってきました. これらは math モジュールに入っている関数です. 第 13 章の最後に出てきた「ライブラリ」のひとつで, 数学の計算をする関数が集められています.

math の使い方
import math          # 数学の道具箱を開ける(1 行目に書く)

print(math.sqrt(2))  # math. を付けて呼ぶ
import math と from turtle import * の違い from turtle import * は「箱の中身を全部取り出して, そのままの名前で使えるようにする」書き方でした. import math は「箱ごと持ってくる」書き方で, 使うときに math. を付けます. math.sqrt と書けば「math の中の sqrt」という意味で, どこから来た関数かがひと目で分かります.
書き方意味
math.sqrt(x)平方根math.sqrt(9) → 3.0
math.pi円周率 π(関数ではなく値)3.141592653589793
math.sin(x)サイン(x はラジアン
math.cos(x)コサイン(x はラジアン
math.tan(x)タンジェント(x はラジアン
math.radians(d)度をラジアンに直すmath.radians(180) → 3.1415…
math.degrees(r)ラジアンを度に直すmath.degrees(math.pi) → 180.0
math.floor(x)切り捨て(小さいほうの整数へ)math.floor(3.7) → 3
math.ceil(x)切り上げ(大きいほうの整数へ)math.ceil(3.2) → 4
math.hypot(a, b)斜辺の長さ(三平方の定理)math.hypot(3, 4) → 5.0
math を付けない関数 abs(x)(絶対値)・round(x, n)(四捨五入)・ max(a, b)min(a, b)len(s) は, math の外にある組み込み関数です (第 13 章 13.6.4 節). import が要らないかわりに,math. も付けません. math.sqrt には math. が要り, abs には要らない.この違いは 「どこの箱に入っている関数か」の違いです.

14.5.1 度とラジアンに気をつける

sin・cos は度では動かない ここが今回いちばんの落とし穴です. math.sinmath.cosmath.tan に渡す角度の単位は 度ではなくラジアンです. タートルの lt(90)rt(90) は度なので, うっかり同じ感覚で書くと,エラーも出ないまま間違った絵になります.
radians.py
import math

print("math.sin(90)          =", math.sin(90))
print("math.sin(radians(90)) =", math.sin(math.radians(90)))

print("math.cos(0)           =", math.cos(0))
print("math.cos(radians(60)) =", math.cos(math.radians(60)))

print("180 度は", math.radians(180), "ラジアン")
print(math.pi, "ラジアンは", math.degrees(math.pi), "度")

math.sin(90)0.894… という, まるで見当違いの値になります.エラーは出ません. 90 度のサインは 1 のはずですから,これはバグです. 三角関数に角度を渡すときは,必ず math.radians() をかぶせる. そう覚えてください.

ラジアン書き方
0 度0math.radians(0)
30 度π/6 ≒ 0.524math.radians(30)
90 度π/2 ≒ 1.571math.radians(90)
180 度π ≒ 3.142math.radians(180)
360 度2π ≒ 6.283math.radians(360)

14.5.2 sin で波を描く

math.sin を「x の場所での波の高さを返す関数」として使うと, タートルで波が描けます. math.sin が返すのは -1 から 1 の値なので, 80 倍して見える大きさにしています.

wave.py
from turtle import *
import math

# 横位置 x のところの波の高さを返す
def wave_y(x):
    return 80 * math.sin(math.radians(x * 2))

ht()
pensize(3)
pencolor("teal")

pu()
goto(-280, wave_y(-280))
pd()

for i in range(141):
    x = -280 + i * 4
    goto(x, wave_y(x))

done()
図 14.7 サインカーブ.高さを返す関数を 1 つ作っただけ.
やってみよう math.radians(x * 2)2 を変えると波の細かさが, 80 を変えると波の高さが変わります. math.cos に変えるとどうなるか, math.sin(radians(x)) + math.sin(radians(x * 3)) のように 2 つ足すとどうなるかも試してみてください.
虫食い虫食い 14-D 星の頂点の座標を返す

星(五芒星)の i 番目の頂点の座標を返す関数を作り, 線でつないで星を描きます. 頂点は 144 度ずつ離れているのでしたね(第 2 章). ____ を埋めてください. math.cosmath.sin に渡す前に,何をかぶせるか忘れずに.

fill13_star.py
from turtle import *
import ____

pensize(3)
pencolor("gold")
ht()

# 星の i 番目の頂点の座標を返す
def star_point(i):
    deg = 90 + i * ____
    x = 160 * math.cos(math.____(deg))
    y = 160 * math.sin(math.____(deg))
    return x, y

x, y = star_point(0)
pu()
goto(x, y)
pd()

for i in range(1, 6):
    x, y = star_point(i)
    goto(x, y)

done()

14.6 関数を組み合わせて組み立てる

第 13 章で,図形を部品に分けて組み立てました. 戻り値があると,計算する部分も部品に分けられます. 「西暦和暦変換プログラム」で見てみましょう.

まず,関数に分けずに書くと,こうなります. 短いプログラムなので,これでも読めます.

関数に分けていない版
print("西暦和暦変換プログラムを開始します")

print("西暦を入力してください")
year = int(input())

if year >= 2019:
    wareki = "令和" + str(year - 2018)
elif year >= 1989:
    wareki = "平成" + str(year - 1988)
elif year >= 1926:
    wareki = "昭和" + str(year - 1925)
else:
    wareki = "不明"

print(year, "年は", wareki, "年です")
print("西暦和暦変換プログラムを終了します")

これを「入力する」「変換する」「表示する」の 3 つに分けます. 変換する部分だけが戻り値を持つところに注目してください.

wareki.py
# 西暦を入力してもらって返す
def input_year():
    print("西暦を入力してください")
    return int(input())

# 西暦を和暦の文字に変えて返す
def to_wareki(year):
    if year >= 2019:
        return "令和" + str(year - 2018)
    elif year >= 1989:
        return "平成" + str(year - 1988)
    elif year >= 1926:
        return "昭和" + str(year - 1925)
    else:
        return "不明"

# 結果を表示する(戻り値は無い)
def show_result(year, wareki):
    print(year, "年は", wareki, "年です")

# 変換をひとまとめにする
def convert_year():
    year = input_year()
    wareki = to_wareki(year)
    show_result(year, wareki)

print("西暦和暦変換プログラムを開始します")
convert_year()
print("西暦和暦変換プログラムを終了します")
入力の打ち方 右コンソールの入力行にあらかじめ 2005 と書いてあります. 実行すると,この行が input() に渡されます. 数を変えて,令和・平成・昭和のそれぞれを試してください.
関数入力(引数)出力(戻り値)役割
input_year()なし西暦(数)人に聞く
to_wareki(year)西暦(数)和暦(文字)計算する
show_result(year, wareki)西暦と和暦なし(None見せる
convert_year()なしなし3 つを順に呼ぶ
入力・計算・出力を分ける 表を縦に見てください. 「人に聞く」「計算する」「見せる」を別々の関数にするのが, プログラムを見通しよくする定石です.
  • 「計算する」関数は printinput もしません.だからそのままテストできます(14.7 節).
  • 画面への出し方を変えたくなっても,直すのは「見せる」関数だけです.
  • 入力をキーボードからファイルに変えても,「計算する」関数はそのまま使えます.
虫食い虫食い 14-E 関数をつないで使う

身長と体重から BMI を計算して判定するプログラムです. 計算する関数判定する関数に分かれています. BMI は「体重 ÷ (身長 m × 身長 m)」で求めます. ____ を埋めてください.

fill13_bmi.py
# 身長(cm)・体重(kg) から BMI を計算して返す
def get_bmi(height, weight):
    m = height / 100
    ____ weight / (m * m)

# BMI から判定のことばを返す
def judge_bmi(bmi):
    if bmi < 18.5:
        return "やせぎみ"
    elif bmi < 25:
        return "ふつう"
    else:
        ____ "ふとりぎみ"

bmi = ____(170, 62)
print("BMI は", round(bmi, 1))
print("判定は", ____(bmi))

14.7 関数をテストする

戻り値のある関数には,うれしい性質があります. 正しく動いているかを,機械的に確かめられるのです. これをテストといいます.

例として,累乗(baseexponent 回かけた数)を 計算する関数を作ります.

power1.py
# base を exponent 回かけた数を返す
def power(base, exponent):
    answer = 1
    for i in range(exponent):
        answer = answer * base
    return answer

print("power(2, 2) =", power(2, 2))
print("power(2, 3) =", power(2, 3))
print("power(2, 4) =", power(2, 4))
print("power(3, 3) =", power(3, 3))

毎回キーボードから入力して確かめるのは大変ですし, どのテストをやったか忘れてしまいます. さらに,プログラムを 1 行直しただけでも, どこが動かなくなるかは予測できません. だから,テストはプログラムに書いて自動化するのが正解です. 上のように並べて呼び出して表示するだけでも,立派なテストです.

14.7.1 テストも関数にする

print の行が長くて,書き換えるのが大変です.テストも関数にしましょう.

power2.py
def power(base, exponent):
    answer = 1
    for i in range(exponent):
        answer = answer * base
    return answer

# 1 件ぶんのテストを表示する
def test(base, exp):
    print("power(", base, ",", exp, ") =", power(base, exp))

test(2, 2)
test(2, 3)
test(2, 4)
test(3, 3)

14.7.2 期待値を書いて,合否まで出す

これでも「答えが合っているか」は自分の目で確かめなければなりません. プログラムに答え(期待値)も書いておき, 合っているかどうかまで表示させましょう.

power3.py
def power(base, exponent):
    answer = 1
    for i in range(exponent):
        answer = answer * base
    return answer

# expected は「こうなるはず」という答え
def test(base, exp, expected):
    answer = power(base, exp)
    if answer == expected:
        result = "PASS"
    else:
        result = "FAIL"
    print("power(", base, ",", exp, ") =", answer, " 判定:", result)

test(2, 2, 4)
test(2, 3, 8)
test(2, 4, 16)
test(3, 3, 27)
test(5, 0, 1)        # 0 乗は 1
test(2, 1, 2)

すべて PASS と出れば安心です. このあと power の中身を書き換えたときも, このプログラムを 1 回実行するだけで, 壊れていないかが一瞬で分かります

端っこを試す テストは「うまくいく場合」だけでなく, 端っこの場合も入れておくと役に立ちます. test(5, 0, 1)(0 乗),test(0, 3, 0)(0 のべき乗), test(2, 1, 2)(1 乗)のように, 「0 のとき」「1 のとき」「いちばん小さいとき」を必ず試してください. バグの多くはここに隠れています.

14.8 この章で出てきた書き方

書き方意味
return 値その値を戻り値として返し,関数はそこで終わる
return a, b2 つ以上の値を並べて返す
x = f(…)戻り値を変数に入れる
print(f(…))戻り値をそのまま表示する
g(f(…))戻り値を別の関数の引数にする
if f(…):真偽値を返す関数を条件に使う
None「値が無い」ことを表す値.return の無い関数が返す
import math数学の関数の入った箱を持ってくる
math.sqrt(x)平方根.math. を付けて呼ぶ
math.radians(d)度をラジアンに直す.sincos に渡す前に必ず使う
round(x, 1)小数第 1 位で四捨五入する
ord(c)文字に割り当てられた番号を返す
今回よくある間違い
症状原因と直し方
SyntaxError: 'return' outside function関数の外で return を書いている.字下げを見直す
None と表示されるreturn を書き忘れている/if のある道筋で return に届いていない/表示するだけの関数を print(…) で囲んでいる
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'None を返す関数の値で計算しようとしている.その関数に return があるか確かめる
NameError: name 'math' is not definedimport math を書き忘れている
NameError: name 'sqrt' is not definedmath. を付け忘れている
計算結果がおかしい(エラーは出ない)sincos に度をそのまま渡している.math.radians() をかぶせる
値が変わらない・使われていないf(3) と呼んだだけで,戻り値を受け取っていない
return のうしろが実行されない仕様どおり.return で関数は終わる

演習

以下の演習には出発点となるプログラムが入っています. 「▶ この演習を開く」で右側に読み込み,それを発展させて完成させてください.

「テストする」の意味 どの演習も,関数を 2 回以上呼んでテストすることを条件にします. 「テストする」とは,14.7 節でやったように プログラムの中からその関数を呼び,結果を表示することです. 余裕があれば,期待値つきの test 関数まで書いてみてください.
演習演習 14-1 年齢計算

生まれ年(西暦)から今年の年齢を計算して返す関数 get_age(year) があります. テストされていることを確かめてから,発展させてください.

  • 「今年」を 2026 と直書きしているのを,引数で渡せるようにする
  • 生まれ年と今年から,学年(小 1・中 2・大 3 など)を返す関数を足す
  • キーボードから生まれ年を入力できるようにする
  • 1900 年より前など,おかしな入力のときは -1 を返す
演習演習 14-2 税込み価格

税抜き価格から税込み価格(消費税 10 %)を計算して返す関数 get_with_tax(price) があります. テストされていることを確かめてから,発展させてください.

  • 税率も引数で渡せるようにする(食品の軽減税率 8 % も計算できるように)
  • 税額だけを返す関数 get_tax(price) を足す
  • 1 円未満を切り捨てる(int() または math.floor()
  • 何個買うかを引数にして,合計金額を返す関数を足す
  • ドーナツ屋さんにする.店内で食べる("eatin")か持ち帰る("takeout")かを文字で受け取って,税率を決めて返す関数 get_rate(category) を足す
どちらが 8 % か 日本の消費税は,持ち帰りが 8 %,店内で食べると 10 % です. 店内で食べるのは「外食」のあつかいになるので,軽減税率が使えないのです. 同じドーナツでも合計金額が変わります. get_rate("たべる") のように, どちらでもない文字が来たときにどうするかも決めておきましょう (-1 を返す,など).
演習演習 14-3 勝率と BMI

勝率(%)を返す関数 get_winning_rate(win, lose) と, BMI を返す関数 get_bmi(height, weight) があります. どちらも「数を受け取って数を返す」関数です. テストされていることを確かめてから,発展させてください.

  • 引き分けの数も引数に加えて,勝率を計算する
  • 勝率から「絶好調」「まずまず」「立て直そう」を返す関数を足す
  • BMI から標準体重(BMI 22 になる体重)を返す関数を足す
  • 0 勝 0 敗のときに割り算でエラーにならないようにする
演習演習 14-4 総和

1 から n までを全部足した数を返す関数 get_sum(n) があります. テストされていることを確かめてから,発展させてください.

  • a から b までの総和を返す関数にする
  • 1 から n までの(階乗)を返す関数 get_product(n) を足す
  • 偶数だけの総和・奇数だけの総和を返す関数を足す(第 7 章の剰余)
  • ガウスの公式 n * (n + 1) / 2 で計算する関数も作り,両方の答えが一致するかテストで確かめる
演習演習 14-5 ランダムとじゃんけん

最小値と最大値の間のランダムな数を返す関数 make_random(low, high) と, じゃんけんの勝敗を返す関数 janken_judge(hand1, hand2) があります. 手は 0=グー,1=チョキ,2=パーで表し, 結果は player1 から見て 1=勝ち・0=あいこ・ -1=負けを返します.

  • 勝敗の数字ではなく「勝ち」「あいこ」「負け」の文字を返す関数を足す
  • 手の数字から「グー」「チョキ」「パー」を返す関数を足す
  • コンピュータとの対戦にして,キーボードから手を入力できるようにする
  • 10 回勝負して,勝った回数を返す関数を足す
ヒント じゃんけんの判定は,hand1 - hand2 の値で場合分けすると楽です. 差が 0 ならあいこ,-12 なら player1 の勝ち, 1-2 なら負けです. random.randint(a, b)a 以上 b 以下の 整数をランダムに返します.
演習演習 14-6 コイン投げのシミュレーション

コイン投げをまねる関数 flip() があります. 2 分の 1 の確率で True(表)を返します. また simulate(n)flip()n 回呼び, 表が出た回数を返します.

  • 試行回数を増やすと,表の割合が 0.5 に近づくことを確かめる(10 回・100 回・1000 回・10000 回で試す)
  • 割合(%)を返す関数を足す
  • サイコロ版 roll() を作り,1 の目が出る割合が 1/6 に近づくか確かめる
  • 表が何回連続で出たかを数える関数を足す
注意 回数を増やしすぎるとブラウザが止まります. 100000 回までにしてください.
自由演習 14-7 戻り値を使った作品

最後は自由制作です. 戻り値のある関数を 2 つ以上作り,それを使って絵を描いてください.

  • 座標を返す関数(return x, y
  • 色を返す関数(return "crimson"
  • 大きさ・角度・長さを計算して返す関数
  • 「もう描いてよいか」を判定して返す関数(return True
ヒント 出発点のプログラムは,色を返す関数座標を返す関数の 2 つを使って,円周上に点を並べています. 半径・個数・色の決め方を変えるだけでも,まったく違う模様になります. math.sin を使って半径を波打たせると,花のような形になります.

まとめ

  • return 値 で,関数から値を持ち帰らせる.その値を戻り値という.
  • 戻り値は,変数に入れる・そのまま print する・計算に使う・別の関数の引数にする,のどれでも使える.受け取らなければ捨てられる
  • return を実行すると,関数はそこで終わる.そのうしろの命令は実行されない.
  • return の無い関数は None を返している.
  • 返せる値の種類に制限は無い.数・文字・真偽値,return a, b で 2 つ以上まとめて返すこともできる.
  • タートルでは,座標・距離・色を計算して返す関数が便利.面倒な計算を関数の中に隠せる.
  • math モジュールには sqrtsincospi などがある.sincos に渡す角度はラジアンなので,math.radians() を忘れない.
  • 「人に聞く」「計算する」「見せる」を別々の関数にすると,計算する関数をテストできる.期待値を書いて PASS/FAIL を出すのが定石.

次の回では,データをまとめて持つリストを習います. 覚えておきたい数が 5 個でも 100 個でも,変数を 100 個作る必要はなくなります. 第 8 章で 1 匹ずつ名前をつけたかめも,リストに入れてしまえば for で一斉に動かせます. ここまでに作った関数と組み合わせると,書ける量が一気に増えます.