プログラミング演習・14章版 第9章 関数・引数・テスト
教材コード

関数・引数・テスト

同じ計算を何度も書くと,直す場所も増える.この章では処理に名前を付けて再利用する方法と,期待した結果を機械的に確認する方法を学ぶ.図9.1は,入力を受け取って結果を返す関数の流れです.

関数を呼び出すときの値の流れ
関数を呼び出すときの値の流れ

関数を定義して値を返す

関数の入力と出力.矢印に沿って値の流れを追う

次の2図は同じ構図です.先に図9.2の空欄を自分で埋めてから,図9.3で確かめる.

関数の引数と返値:書き込む前
関数の引数と返値:書き込んだ後

関数(function)は,名前を付けた処理です.def で定義し,呼び出し時の引数(argument)を受け取り,return で戻り値(return value)を返します.

関数を入力集合から出力集合への対応として考えると,twice は整数 x に 2x を対応させる.Python の関数は,条件分岐や外部状態も扱えるので数学の関数と完全に一致するわけではありません.しかし,入力と出力を先に定めると処理を小さな単位で検証できます.

実験で測定値を一定の係数で換算する場面を考えます.各値に同じ式を繰り返し書く代わりに,換算式に名前を付けて呼び出します.式へ渡す測定値が引数であり,計算後の値が戻り値です.

chapter-09_code_1.py
def twice(x):
    return x * 2

kekka = twice(3)
print(kekka)
実行結果の例
6

1行目は twice という名前と,引数 x を定めます.2行目は x * 2 を呼び出し元へ返します.4行目では 3 が x となり,返された 6 が kekka に入ります.5行目で初めて表示します.

print(x * 2) は画面に表示するが,呼び出し元に計算結果を渡しません.return x * 2 は値を渡すが,それ自体は表示しません.return の後の同じ関数内の文は実行されません.

そのまま演習関数を定義して値を返す · そのまま演習

そのまま演習:twice(5) を表示し,結果を予想してから実行します.

ヒントを見る

twice を呼ぶときの5が仮引数 x に入ります。返り値を kekka に保存する行と、最後に表示する行を順に追います。

演習の解説を見る

解説:x が5になるため,return x * 2 は10を返し,print が10を表示します.

虫食い関数を定義して値を返す · 虫食い

虫食い演習:次の空欄を埋め,3を入れて9が出ることを確認します.

chapter-09_code_3.py
def triple(x):
    return x * ___

print(triple(3))
ヒントを見る

triple は引数を3倍にする関数です。掛ける数を決め、呼び出しの引数とその数を区別してください。

演習の解説を見る

解説:空欄は 3 です.計算式は 3 * 3 となります.

def は処理に名前を付け,return は計算結果を呼び出し元へ渡す.表示が必要な場所で print を使います.

二つの値を受け取る場合

平均を計算するときは,必要な値を二つとも引数として渡す.計算に使う値を関数の外の変数から暗黙に読むより,関数の定義だけで必要な入力が分かります.

chapter-09_code_4.py
def average(a, b):
    goukei = a + b
    return goukei / 2

print(average(70, 90))
実行結果の例
80.0

1行目は入力を a と b の二つに分ける.2行目の合計は160です.3行目は160を2で割り,浮動小数点数80.0を返します.5行目がそれを表示します.average(70) では引数が一つ不足し,TypeError になります.

次の誤りを比べる:def average(a, b): print((a+b)/2) と定義すると画面には平均を出すが,kekka = average(70, 90) の kekka は None になります.平均を後の計算に使う場合は return (a+b)/2 に直します.

そのまま演習二つの値を受け取る場合 · そのまま演習

そのまま演習:average(60, 80) を表示します.

ヒントを見る

二つの引数を足す処理と、合計を2で割る処理を分けて追います。整数どうしでも / の返り値は小数の型です。

演習の解説を見る

解説:合計140を2で割り,70.0を表示します.

虫食い二つの値を受け取る場合 · 虫食い

虫食い演習:return (a + b) / ___ の空欄を埋める.

ヒントを見る

平均は合計を値の個数で割ります。a と b の二つの値があるので、足した後に何で割るか考えてください。

演習の解説を見る

解説:平均する値が二つなので,空欄は 2 です.

局所変数とスコープ

関数の中で代入した名前は,原則として関数の中で使う局所変数(local variable)です.名前を参照できる範囲をスコープ(scope)といいます.

呼び出しごとに関数の実行枠が作られ,その枠に局所変数が結びつく.外側にも同じ綴りの名前があっても,関数内で代入した名前は別の束縛です.この区別により,他の処理が使う変数を不用意に書き換えずに済む.

別々の集計表に同じ列名 x があっても,その値は別です.関数の中の x と外の x も,名前が同じでも別々に管理されます.関数の計算結果を外で使うには return を使います.

chapter-09_code_6.py
x = 10

def sample():
    x = 3
    return x

print(sample(), x)
実行結果の例
3 10

1行目で外側の x を10とします.4行目の代入は関数内の別の x に3を入れます.5行目はその3を返します.最後の print は戻り値3と外側の10を順に表示します.

そのまま演習局所変数とスコープ · そのまま演習

そのまま演習:関数内の x = 3 を x = 7 に変え,最後の表示を予想します.

ヒントを見る

sample の内側だけで x を7に変更します。返り値を受け取っても、外側の x へ代入したことにはなりません。

演習の解説を見る

解説:表示は 7 10 です.関数内の代入は外側の10を変更しません.

虫食い局所変数とスコープ · 虫食い

虫食い演習:関数内で計算した合計を外へ渡す語を補います.

chapter-09_code_8.py
def add(a, b):
    goukei = a + b
    ___ goukei
ヒントを見る

goukei を表示するのではなく、関数を呼び出した場所で使える値として渡す行です。計算結果を返す文を考えます。

演習の解説を見る

解説:空欄は return です.goukei は関数の外から直接読まず,戻り値として受け取ります.

関数の外で print(goukei) を実行すると,外側に同名の変数を定義していない限り NameError になります.

実行を一段ずつ追う

関数呼び出しで迷うときは,外側と内側の変数を別々の欄に書きます.次の例では,同じ関数を2回呼んでも,1回目の局所変数は2回目へ持ち越されません.

chapter-09_code_9.py
def add_one(x):
    kekka = x + 1
    return kekka

print(add_one(2))
print(add_one(5))
実行結果の例
3
6

1回目は x=2,kekka=3 となって3を返します.2回目は新たに x=5,kekka=6 となって6を返します.関数内の kekka は外側の変数として残りません.

そのまま演習実行を一段ずつ追う · そのまま演習

そのまま演習:add_one(-1) の返値を予想して実行します.

ヒントを見る

追加する呼び出しでは、負の引数にも同じ式を使います。最初の表示を残し、二度目の返り値を次の行へ表示します。

演習の解説を見る

解説:関数内で −1 + 1 = 0 を計算し,0を返します.

虫食い実行を一段ずつ追う · 虫食い

虫食い演習:def add_one(x): return x + ___ の空欄を補います.

ヒントを見る

add_one は受け取った数の次の整数を返します。増やす量と、呼び出し時の x の値を区別します。

演習の解説を見る

解説:空欄は 1 です.外側の変数に依存せず,引数から計算します.

引数の初期値とキーワード引数

引数には初期値(default value)を与えられます.呼び出し時に名前を示すキーワード引数(keyword argument)を使えば,値の意味も読み取りやすい.

引数の初期値は,その引数を省略した呼び出しに適用されます.位置引数は定義の順に対応し,キーワード引数は名前によって対応します.変更可能なリストを初期値にすると呼び出し間で状態を共有しうるため,必要なら None を初期値とし,関数内で新しいリストを作ります.

アンケート結果の表示形式を共通化する場面を考えます.通常の敬称を「さん」と決めておき,必要な呼び出しだけ suffix="先生" と指定すると,その表示だけを変えられます.

chapter-09_code_11.py
def greet(namae, suffix="さん"):
    return namae + suffix

print(greet("青木"))
print(greet("青木", suffix="先生"))
実行結果の例
青木さん
青木先生

1行目の suffix="さん" が省略時の値です.2行目は二つの文字列を連結します.4行目は初期値を使います.5行目は suffix を名前で指定して上書きします.

そのまま演習引数の初期値とキーワード引数 · そのまま演習

そのまま演習:greet("田中", suffix="君") の結果を予想して実行します.

ヒントを見る

省略した suffix は定義時の初期値、指定した suffix は呼び出し時の値になります。名前と敬称を別々に確認します。

演習の解説を見る

解説:namae が「田中」,suffix が「君」なので,「田中君」と表示します.

虫食い引数の初期値とキーワード引数 · 虫食い

虫食い演習:省略時に「様」を付ける定義へ直します.

chapter-09_code_13.py
def greet(namae, suffix=___):
    return namae + suffix
ヒントを見る

suffix を省略した呼び出しでも、名前の後ろに敬称が付くようにします。文字列の初期値なので引用符も必要です。

演習の解説を見る

解説:空欄は "様" です.文字列なので引用符が必要です.

def greet(namae="客", suffix): は,初期値のある引数の後に初期値のない引数を置いているため構文エラーになります.def greet(namae, suffix="さん"): と並べる.また,greet("青木", namae="田中") は同じ引数に二つの値を与えるため失敗します.

変更可能な初期値に注意する

リストを初期値に直接指定すると,省略した呼び出し同士で同じリストを使います.毎回新しいリストを必要とする場合は,次のようにします.

chapter-09_code_14.py
def append_name(namae, names=None):
    if names is None:
        names = []
    names.append(namae)
    return names

print(append_name("A"))
print(append_name("B"))
実行結果の例
['A']
['B']

1行目では省略時の目印を None にします.2行目と3行目は省略された場合だけ空リストを新しく作ります.4行目で名前を追加し,5行目でそのリストを返します.2回の呼び出しでリストが別々に作られるので,2回目の結果にAは混ざりません.

そのまま演習変更可能な初期値に注意する · そのまま演習

そのまま演習:append_name("C") をもう一度呼び,表示します.

ヒントを見る

引数 names を省略するたびに新しいリストが作られます。三度目の呼び出しでも、前の呼び出しの名前は引き継がれません。

演習の解説を見る

解説:新しいリストにCだけを入れるので ['C'] です.

虫食い変更可能な初期値に注意する · 虫食い

虫食い演習:if names is ___: names = [] の空欄を補います.

ヒントを見る

names が未指定を表す特別な値のときだけ、新しい空リストを作ります。空リストそのものとの比較ではありません。

演習の解説を見る

解説:空欄は None です.値そのものを比較する is None を使います.

テストとデバッグ

期待値と実際の結果を照合するテストの流れ

テスト(test)は入力と期待する出力を照合する作業です.assert は条件が偽の場合に AssertionError を起こす.デバッグ(debug)は不具合の原因を特定して直す作業です.

テストケースは通常の値だけでなく,境界値と例外的な値を含める.2倍の関数なら正数だけでなく0と負数も試す.失敗時には,期待値の誤り,関数の条件分岐,値の型の順に確認すると原因を絞りやすい.テストが通っても,未検査の入力に対する正しさまで証明したことにはなりません.

電卓を点検するとき,2を押して4が出るだけでは安心できません.0を押した場合と負の数を押した場合も確認します.予想した答えを書き,実際の答えと比べることがテストです.

chapter-09_code_16.py
def double(x):
    return x * 2

assert double(3) == 6
assert double(0) == 0
assert double(-2) == -4
print("テスト通過")
実行結果の例
テスト通過

1行目と2行目で検査対象を定義します.4行目は正数,5行目は0,6行目は負数を試す.どの条件も真なので7行目まで進みます.どれか一つでも偽なら,そこで止まり最後の表示は出ません.

assert double(3) = 6 は構文エラーです.比較には == を使います.また assert は開発中の確認に適するが,利用者の入力の検査をこれだけに任せません.

そのまま演習テストとデバッグ · そのまま演習

double(5) == 10 を assert で検査し、成功したら「5のテスト通過」と表示してください。

ヒントを見る

新しい検査を既存の assert の後へ追加します。成功を知らせる表示は、追加した検査より後へ置きます。

演習の解説を見る

解説:double(5) は10であり,条件が真なので処理が続く.

虫食いテストとデバッグ · 虫食い

虫食い演習:負数の期待値を補います.

chapter-09_code_18.py
assert double(-3) == ___
ヒントを見る

負の引数でも double の計算式は変わりません。符号を含めて2倍を計算し、比較の右側に置きます。

演習の解説を見る

解説:空欄は -6 です.負の3に2を掛けるためです.

失敗したテストから原因を探す

テストの期待値を先に書くと,不具合が見つかりやすい.次の例では,2を入力した場合の料金は240のはずです.

chapter-09_code_19.py
def fee(n):
    return n + 120

assert fee(2) == 240

2行目の式は 2 + 120 を計算して122を返します.4行目は122と240を比較して偽となり,AssertionError が発生します.「1件120円を人数分」という仕様なら,2行目を return n * 120 に直します.すると fee(2) は240で,テストを通る.

失敗した assert を消して見かけ上の成功にしません.期待値と仕様を確認し,必要なら一時的に print(fee(2)) で実際の値を出します.原因が分かったら式を直し,テストを再実行します.

そのまま演習失敗したテストから原因を探す · そのまま演習

fee の式を修正し、fee(0) と fee(3) を assert で検査してから両方の値を1行に表示してください。

ヒントを見る

単価と件数を掛ける fee の式を先に確認します。0件と3件の検査を通してから、両方の返り値を表示します。

演習の解説を見る

解説:assert fee(0) == 0 と assert fee(3) == 360 がどちらも通る.

虫食い失敗したテストから原因を探す · 虫食い

虫食い演習:assert fee(4) == ___ の空欄を補います.

ヒントを見る

1件の料金に件数4を掛けた値が期待値です。関数の式と同じ計算を行ってから検査します。

演習の解説を見る

解説:4 × 120 = 480 なので空欄は 480 です.

関数を別の関数に渡す

Python では関数を値として別の関数へ渡せる.これを高階関数(higher-order function)の利用といいます.lambda は短い無名関数(anonymous function)をその場で作ります.

sorted の key は,要素から比較用の値を作る関数を受け取ります.map は各要素に関数を適用し,filter は条件が真の要素を選ぶ.両者の返値はイテレータなので,一覧として表示するには list で具体化します.処理が複雑なら def で名前を付けると読みやすい.

名簿を並べる係に「名前の長さで並べて」と指示する場面を考えます.sorted に名簿と「長さを測る道具」len を渡せば,係が一人ずつ長さを測って並べる.

chapter-09_code_20.py
words = ["bbb", "a", "cc"]
print(sorted(words, key=len))
print(list(map(lambda x: x * 2, [1, 2, 3])))
print(list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4])))
実行結果の例
['a', 'cc', 'bbb']
[2, 4, 6]
[2, 4]

1行目で文字列を用意します.2行目では len が各文字列の長さを返し,短い順に並べる.3行目では各整数を2倍し,list で結果を表示可能にします.4行目では2で割った余りが0の値だけを残す.

そのまま演習関数を別の関数に渡す · そのまま演習

そのまま演習:sorted(words, key=len) の words を ["dddd", "b", "cc"] に変え,順を予想します.

ヒントを見る

文字列を差し替えた影響は文字数順の並べ替えに現れます。map と filter の数値データは変更しません。

演習の解説を見る

解説:長さは4,1,2なので,結果は ["b", "cc", "dddd"] です.

虫食い関数を別の関数に渡す · 虫食い

虫食い演習:文字列を長さで並べる関数を補います.

chapter-09_code_22.py
print(sorted(["bbb", "a", "cc"], key=___))
ヒントを見る

文字列そのものの辞書順ではなく、文字数を比較するための関数を key に渡します。関数をその場で呼び出す括弧は付けません。

演習の解説を見る

解説:空欄は len です.len() と書くと引数のない呼び出しとなるので,関数そのものを渡す.

sorted(words, key=len()) は len を先に引数なしで呼び出す誤りです.key=len なら sorted が要素ごとに len を呼ぶ.map と filter の結果はそのままではリストではないので,初学者が内容を確認するときは list(...) で包む.

並べ方の基準を変える

key に小さな関数を渡すと,元の値と別の尺度で並べられます.

chapter-09_code_23.py
values = [3, 1, 2]
print(sorted(values, key=lambda x: -x))
print(values)
実行結果の例
[3, 2, 1]
[3, 1, 2]

1行目は元のリストを作ります.2行目は各値を負にして比較するので,元の数が大きいものから並ぶ.sorted は新しいリストを返し,3行目から分かるように元の values は変えません.

そのまま演習並べ方の基準を変える · そのまま演習

そのまま演習:key=lambda x: x に変え,並び方を確かめる.

ヒントを見る

key の符号を変えると、新しい並べ替え結果の順番が変わります。sorted は元の values を変更しません。

演習の解説を見る

解説:元の値をそのまま比較するため,[1, 2, 3] となります.

虫食い並べ方の基準を変える · 虫食い

虫食い演習:大きい順に並べる式の空欄を補う:key=lambda x: ___x.

ヒントを見る

大きい数から並べるため、key が返す比較用の値の大小を逆転させます。x の前に置く符号を考えてください。

演習の解説を見る

解説:空欄は負号 - です.比較用の値が −3, −1, −2 となるため,3,2,1の順になります.

関数を引数として渡すと,並べ方や処理内容を呼び出し側から指定できます.lambda は短い式に使い,複数の段階がある処理には def を使います.

用語を確かめる

関数

読み:かんすう.英語:function.語源は「働き」を表すラテン語にさかのぼる.関連語:機能,関数呼び出し.発表では「入力を受けて値を返す function」と説明できます.

引数

読み:ひきすう.英語:argument.数学で関数に与える値にも使う語です.関連語:仮引数,実引数.発表では「function に渡す argument」といえます.

戻り値

読み:もどりち.英語:return value.return は「戻る」の意です.関連語:return 文,呼び出し元.

局所変数

読み:きょくしょへんすう.英語:local variable.local は「その場所の」という意です.関連語:スコープ,大域変数.

スコープ

読み:すこーぷ.英語:scope.「見える範囲」を表す語です.関連語:局所変数,大域変数.

初期値

読み:しょきち.英語:default value.default は「指定がないときの標準」を意味します.関連語:引数,省略.

キーワード引数

読み:きーわーどひきすう.英語:keyword argument.keyword は値を指定する名前を指す.関連語:位置引数,初期値.

テスト

読み:てすと.英語:test.「試す」ことから来る.関連語:テストケース,境界値.

デバッグ

読み:でばっぐ.英語:debug.bug は不具合を指し,de は取り除く意です.関連語:エラー,例外.

高階関数

読み:こうかいかんすう.英語:higher-order function.関数を引数や戻り値に扱う関数です.関連語:map,filter.

無名関数

読み:むめいかんすう.英語:anonymous function.anonymous は「名前を出さない」の意です.関連語:lambda,高階関数.

用語を確かめる:言葉とコードの動きを結び付けます

知識

関数(function)は、名前を付けて再利用する処理です。呼び出すときに渡す値が引数(argument)、returnで呼び出し元へ返す値が戻り値(return value)です。関数内のresultは局所変数で、呼び出すたびにその呼び出しの計算に使われます。

def twice(x):
    result = x * 2
    return result

value = twice(3)
print(value)
print(twice(-2))
そのまま演習用語を確かめる:言葉とコードの動きを結び付けます

twice(3)から返る値と、twice(-2)から返る値を別々の行に表示する例です。実行前に各行の値を予想し、上のコードを変更せずに実行してください。その後「出力例」を開いて比較します。入力はありません。関数の中へprintを追加せず、呼び出し元で戻り値を表示します。

出力例
6
-4
ヒントを見る

最初の関数呼び出しで渡す値と、次の呼び出しで渡す値を分けて追ってください。関数内で計算した値が戻った後、どの表示命令へ渡されるかを確認します。

演習の解説

twice(3)ではxが3、resultが6になり、returnで6が戻ります。その6をvalueに代入し、print(value)で表示します。次のtwice(-2)では別の呼び出しのxが-2、resultが-4になり、-4が戻って表示されます。return自体は画面へ表示しません。

虫食い用語を確かめる:言葉とコードの動きを結び付けます

___の1か所に、resultを呼び出し元へ返すPythonのキーワードを補ってください。ほかの行は変更せず、完成例と同じ2行を表示してください。

def twice(x):
    result = x * 2
    ___ result

value = twice(3)
print(value)
print(twice(-2))
ヒントを見る

引数xへ入る数を、呼び出しごとに区別してください。関数内で計算したresultを呼び出し元へ渡す文と、画面へ表示する文は役割が異なります。

虫食いの解説
def twice(x):
    result = x * 2
    return result

value = twice(3)
print(value)
print(twice(-2))

空欄には return が入ります。twice(3)ではxが3、resultが6になり、returnで6が戻ります。その6をvalueに代入し、print(value)で表示します。次のtwice(-2)では別の呼び出しのxが-2、resultが-4になり、-4が戻って表示されます。return自体は画面へ表示しません。

図で理解する

関数の入出力
関数の入出力
テストの流れ
テストの流れ
関数の引数と返値:考える前
関数の引数と返値:考える前
関数の引数と返値:書き込んだ後
関数の引数と返値:書き込んだ後

練習して確かめる

小さな関数にまとめる

知識

繰り返し使う計算は def で関数にし、結果を return で返します。print は表示、return は呼び出し元への返値という違いがあります。

chapter-09_code_25.py
def twice(x):
    return x * 2
print(twice(4))
そのまま演習小さな関数にまとめる · そのまま演習

twice(7) の結果を1行で表示してください。

ヒントを見る

twice の計算式は残して、呼び出しの引数を7にします。関数内部の仮引数に入る値から計算します。

演習の解説

引数 x に 7 が入り、return で 14 が返る。

虫食い小さな関数にまとめる · 虫食い

空欄を埋め、コードを実行してください。

chapter-09_code_26.py
def twice(x):
    ___ x * 2
ヒントを見る

x の2倍を、表示せずに呼び出し元へ渡します。関数の結果を返す文の位置が空欄です。

虫食いの解説
chapter-09_code_27.py
def twice(x):
    return x * 2

return を補います。

関数の局所変数とスコープ

知識

関数内で作った変数は通常、その関数の中でだけ使えます。同じ名前の外側の変数とは別に扱われます。

chapter-09_code_28.py
x = 10
def f():
    x = 3
    return x
print(f(), x)
そのまま演習関数の局所変数とスコープ · そのまま演習

局所変数と大域変数を比べ、既存の表示に続けて x - f() を1行で表示してください。

ヒントを見る

f が返す局所変数の値と、大域変数 x の値を区別します。追加する差の表示は既存の表示とは別の行です。

演習の解説

f の中の x は 3、外側の x は 10 なので 3 10 と出ます。

虫食い関数の局所変数とスコープ · 虫食い

空欄を埋め、コードを実行してください。

chapter-09_code_29.py
def square(x):
    result = x * x
    ___ result
ヒントを見る

局所変数 result は関数の外から直接参照できません。計算した値を呼び出し元へ渡す文を置きます。

虫食いの解説
chapter-09_code_30.py
def square(x):
    result = x * x
    return result

return を補います。result は関数の外から直接参照できません。

assert とテストケース

知識

assert は予想した条件が偽なら AssertionError を出します。正常例だけでなく境界値や異常例も用意すると、誤りを見つけやすい。

chapter-09_code_31.py
def double(x):
    return x * 2
assert double(3) == 6
そのまま演習assert とテストケース · そのまま演習

double(0) と double(-2) を assert で検査し、成功したら2つの返値を1行に表示してください。

ヒントを見る

0と負の引数でも同じ2倍の規則を検査します。assert 自体は表示しないため、最後に返り値を明示して表示します。

演習の解説

0 と負数も確認し、入力の範囲による見落としを減らす。

虫食いassert とテストケース · 虫食い

空欄を埋め、コードを実行してください。

chapter-09_code_32.py
assert double(0) ___ 0
ヒントを見る

左側の計算結果と右側の期待値が同じかを調べます。代入ではなく、真理値を返す比較です。

虫食いの解説
chapter-09_code_33.py
assert double(0) == 0

== を補います。

デバッグの順序

知識

不具合があれば再現できる小さな入力を決め、期待値と実際の値を比べます。途中の値を表示するかテストで確かめ、原因を1つずつ絞る。

chapter-09_code_34.py
def fee(n):
    return n * 120
assert fee(2) == 240
そのまま演習デバッグの順序 · そのまま演習

fee(0) と fee(1) を assert で検査し、成功したら返値を1行に表示してください。

ヒントを見る

件数0と1をそれぞれ検査します。検査の条件と、成功後に表示する二つの返り値を対応させます。

演習の解説

境界の 0 と最小の正数 1 を調べると、計算式の取り違えに気づきやすい。

虫食いデバッグの順序 · 虫食い

空欄を埋め、コードを実行してください。

chapter-09_code_35.py
def fee(n):
    return n * 120
___ fee(1) == 120
ヒントを見る

条件が偽のときに検査の失敗を知らせる文です。ここでは fee の返り値が期待値と等しいことを検査します。

虫食いの解説
chapter-09_code_36.py
def fee(n):
    return n * 120
assert fee(1) == 120

assert を補います。

エラーを読む

知識

エラー表示は失敗した行と種類を教える。SyntaxError は文法、NameError は未定義の名前などを示します。最後の行から読み、該当するコードに戻る。

chapter-09_code_37.py
value = 4
print(value)
そのまま演習エラーを読む · そのまま演習

誤って valeu と書いた場合の NameError を確認し、正しい value を用いて5を1行で表示してください。

ヒントを見る

未定義の名前を使った最初の状態と、定義済みの value に直した完成状態を区別します。正しい名前を使い、表示する値へ1を加えます。

演習の解説

未定義の valeu を使うと NameError になります。完成コードでは定義済みの value を使い、4 + 1 を計算するため、5を1行で表示します。

虫食いエラーを読む · 虫食い

空欄を埋め、コードを実行してください。

chapter-09_code_38.py
value = 4
print(___)
ヒントを見る

表示する名前は、直前に代入した変数名と一致させます。英字の並びを1文字ずつ確かめてください。

虫食いの解説
chapter-09_code_39.py
value = 4
print(value)

定義した名前と同じ value を補います。

引数の初期値とキーワード引数

知識

関数の引数には初期値を設定できます。呼び出すときに名前を明示すれば、意味が読み取りやすくなります。変更可能なリストなどを初期値に直接置くことは避ける。

chapter-09_code_40.py
def greet(name, suffix='さん'):
    return name + suffix
print(greet('青木', suffix='先生'))
そのまま演習引数の初期値とキーワード引数 · そのまま演習

suffix を省略した greet('青木') の結果を1行で表示してください。

ヒントを見る

今回の呼び出しには suffix の指定がありません。関数定義に書かれた敬称の初期値を使って連結します。

演習の解説

省略時は初期値の『さん』が使われます。

虫食い引数の初期値とキーワード引数 · 虫食い

空欄を埋め、コードを実行してください。

chapter-09_code_41.py
def greet(name, suffix=___):
    return name + suffix
ヒントを見る

第2引数を省略したときの敬称を定義します。名前と連結するので、初期値も文字列にします。

虫食いの解説
chapter-09_code_42.py
def greet(name, suffix='さん'):
    return name + suffix

文字列の初期値を補います。

高階関数とラムダ式

知識

関数は変数に渡せる値であり、sorted の key や map・filter に指定できます。lambda は短い無名関数を作ります。複雑な処理は def で名前を付ける。

chapter-09_code_43.py
values = [3, 1, 2]
print(sorted(values, key=lambda x: -x))
そのまま演習高階関数とラムダ式 · そのまま演習

key=lambda x:x に変えて順番を比べます。

ヒントを見る

無名関数は要素そのものを比較用の値として返します。負号を付けた場合との並び順の違いを確認します。

演習の解説

負の値を基準にすると降順、x そのものを基準にすると昇順。

虫食い高階関数とラムダ式 · 虫食い

空欄を埋め、コードを実行してください。

chapter-09_code_44.py
print(sorted([3,1,2], key=___ x: -x))
ヒントを見る

key の空欄は、引数と式だけで小さな無名関数を作る構文です。コロンの右の -x が比較用の値になります。

虫食いの解説
chapter-09_code_45.py
print(sorted([3,1,2], key=lambda x: -x))

lambda を補います。

章末演習(10問)

ここには解答を載せていません。

  1. 章末演習章末演習 1

    【基本】身長と体重から BMI を求める関数を定義し,固定データ3人分の BMI を表示してください。

    入力

    入力なし(input は使わない)。プログラムの中に次の変数を書きます。各要素は [身長(cm), 体重(kg)] の正の整数。 data = [[170, 65], [160, 50], [182, 90]]

    処理条件

    • 関数 bmi(height_cm, weight_kg) を def で定義します。関数の中で身長を 100 で割って m に直し,BMI=体重÷(身長[m])² を計算します。
    • 計算結果は round(値, 1) で小数第1位に丸め,その float を return します。関数の中では print しません。
    • 表示は関数の外の for 文で行います。data の要素ごとに bmi を呼ぶ。
    • 外部ライブラリは使いません。身長が 0 以下のデータは与えないので,その場合の処理は不要。

    出力

    • data の順に3行を表示します。各行は「身長cm 体重kg -> BMI 値」の形とします。文字・数字・記号はすべて半角,区切りは半角空白1個とします。cm と kg は数字の直後に空白を入れずに続ける。
    • BMI の値は round の戻り値をそのまま表示する(例:22.5)。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    170cm 65kg -> BMI 22.5
    160cm 50kg -> BMI 19.5
    182cm 90kg -> BMI 27.2
    
    ヒントを見る

    関数の中身を「単位をそろえる」「式に当てはめる」「丸めて返す」の3段階に分けて考えましょう。表示を関数の外に置くと,同じ関数を別の計算にも使い回せます。

  2. 章末演習章末演習 2

    【基本】リストの中で基準値より大きい要素の個数を返す関数を作り,関数の中の変数と外の変数が別物であることを確かめてください。

    入力

    入力なし(input は使わない)。プログラムの中に次の値を書きます。 - values = [3, 8, 5, 10, 1](整数のリスト) - limit = 4(整数) - 関数を呼ぶ前に,関数の外で count = 100 と代入しておく。

    処理条件

    • 関数 count_over(values, limit) を def で定義します。関数の中で局所変数 count を 0 から始める。for 文と if 文で limit より大きい要素の個数を数えて return します。limit と等しい要素は数えません。
    • global 宣言は使いません。sum・len と内包表記の組み合わせや,filter による数え上げも使いません。
    • values が空リストのときは 0 を返す(表示はしません。assert で確かめてよい)。
    • 関数の戻り値を変数 result に受け取ってから表示します。

    出力

    • 2行を表示します。1行目は「count_over -> 個数」,2行目は「関数の外の count = 値」とします。
    • 英字・数字・記号・空白は半角とし,日本語部分は実行例どおりにします。区切りの空白は半角1個とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    count_over -> 3
    関数の外の count = 100
    
    ヒントを見る

    関数の中で代入した名前が関数の外の名前とどう関係するかを,呼び出しの前と後で外の count を確認して考えましょう。基準値と等しい要素を数えるかどうかは,比較演算子の選び方で決まります。

  3. 章末演習章末演習 3

    【基本】番号を決まった桁数の文字列ラベルにする関数を作り,既定値付き引数とキーワード引数の働きを確かめてください。

    入力

    入力なし(input は使わない)。number には 0 以上の整数を渡す。

    処理条件

    • 関数 make_label(number, prefix="No.", width=3) を def で定義し,ラベル文字列を return します。
    • number を文字列にした桁数が width より少ないときだけ,左側を半角の 0 で埋めて width 桁にします。width 以上の桁数ならそのまま使います。最後に prefix を先頭に付ける。
    • 0 埋めは len と文字列の繰り返し(*)で作ります。zfill,rjust,書式指定(:03 や :0>3 など)は使いません。
    • 次の4通りをこの順に呼び出して表示する:make_label(7),make_label(42, prefix="ID-"),make_label(5, width=2),make_label(1234)。

    出力

    • 4行を表示します。各行にはラベル文字列だけを表示します。英数字・記号はすべて半角とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    No.007
    ID-042
    No.05
    No.1234
    
    ヒントを見る

    まず数字を文字列にして何文字あるかを調べ,目標の桁数との差がいくつかを考えましょう。差が0以下のときに何もしなければ,桁数の多い番号もそのまま扱えます。

  4. 章末演習章末演習 4

    【応用】点数から評価を返す関数を作り,境界値を重点的に調べるテストを assert で書いてください。

    入力

    入力なし(input は使わない)。score は 0 以上 100 以下の整数とします。テストに使う入力と期待値の組は次の10件とします。 100→"S",90→"S",89→"A",80→"A",79→"B",70→"B",69→"C",60→"C",59→"F",0→"F"

    処理条件

    • 関数 grade(score) を def で定義します。if / elif / else で次の文字列(半角大文字1字)を return する:90以上は "S",80以上は "A",70以上は "B",60以上は "C",それ未満は "F"。
    • 上の10件を [入力, 期待値] の組のリストにまとめる。for 文で1件ずつ assert grade(入力) == 期待値 を実行します。
    • 表示する件数は len でリストから求めます。10 を直接書きません。
    • 辞書による表引きや外部ライブラリによる判定は使いません。

    出力

    • すべての assert が通ったら,「(len で求めた件数)件のテストに合格」を1行だけ表示します。数字は半角,ほかは全角とします。
    • assert が失敗したときは AssertionError で止まってよいでしょう。
    • 各テストの途中経過,見出し,空行,デバッグ用の表示は付けません。

    実行例

    10件のテストに合格
    
    ヒントを見る

    条件を大きい基準から順に調べると,elif の各段では「それより上ではない」ことが保証されます。テストでは各評価の下限ちょうどの点と,その1点下を組にして選ぶと,境界の誤りを見つけやすくなります。

  5. 章末演習章末演習 5

    【応用】誤りのある平均値の関数を修正し,空のリストにも対応させてください。

    入力

    入力なし(input は使わない)。次の誤った関数を出発点とします。

    def average(xs):
        total = 0
        for x in xs:
            total = x
        return total / len(xs)
    

    テストに使うリストは [2, 4, 9],[7],[] の3つ(この順)。

    処理条件

    • 上の関数を右側のエディタに写し,まず average([2, 4, 9]) が誤った値 3.0 になることを確かめる。この確認用の表示は,提出するコードから消す。
    • 修正後の average(xs) は,xs の平均値を float で return します。xs が空リストのときは割り算をせずに None を return します。
    • 合計は for 文で1つずつ足して求めます。sum や statistics モジュールは使いません。
    • 3つのリストを for 文で順に関数へ渡し,結果を表示します。

    出力

    • 3行を表示します。各行は「average(リスト) -> 結果」の形とします。リストは Python が print する表記(例:[2, 4, 9])とし,結果は丸めずに Python の既定の表示(5.0 や None)とします。
    • 英数字・記号・空白はすべて半角とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    average([2, 4, 9]) -> 5.0
    average([7]) -> 7.0
    average([]) -> None
    
    ヒントを見る

    ループの中で total の値を一時的に表示し,繰り返すたびに合計が増えているかを観察しましょう。空のリストのとき割り算の分母が何になるかを考え,割り算より前に関数を抜ける条件を置きます。

  6. 章末演習章末演習 6

    【応用】関数を引数として受け取り,リストの各要素に適用する関数を作ってください。

    入力

    入力なし(input は使わない)。プログラムの中に values = [1, 2, 3, 4] を書きます。

    処理条件

    • 関数 apply_all(func, values) を def で定義します。空のリスト result を用意し,for 文で各要素に func を適用した値を append して,その新しいリストを return します。引数の values 自体は書き換えません。
    • 2乗を返す square(x) と,2 で割った値(/ による float)を返す half(x) を def で定義します。
    • apply_all には関数そのもの(square や half)を渡す。map,内包表記,lambda は使いません。
    • values が空リストなら空リストを返す(表示はしません。assert で確かめてよい)。

    出力

    • 2行を表示します。1行目は「square: リスト」,2行目は「half: リスト」とします。リストは Python が print する表記とし,コロンの後に半角空白を1個入れます。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    square: [1, 4, 9, 16]
    half: [0.5, 1.0, 1.5, 2.0]
    
    ヒントを見る

    関数名の後ろに丸かっこを付けるとその場で呼び出され,付けなければ関数そのものを渡せます。どこで呼び出すべきかを考えましょう。

  7. 章末演習章末演習 7

    【応用】自分で定義した関数を sorted の key 引数に渡し,50 との差が小さい順と大きい順に並べ替えてください。

    入力

    入力なし(input は使わない)。プログラムの中に data = [10, 48, 70, 55, 30] を書きます。

    処理条件

    • 50 との差の大きさ(絶対値)を返す関数 distance_from_50(x) を def で定義します。
    • sorted(data, key=distance_from_50) で差が小さい順のリストを作ります。さらに reverse=True を付けて,差が大きい順のリストを作ります。
    • 差が同じ要素(70 と 30)は,元の data での順序を保つ(sorted の安定性による)。
    • data 自体は書き換えません。data.sort() と lambda は使いません。

    出力

    • 3行を表示します。1行目は差が小さい順のリスト,2行目は差が大きい順のリスト,3行目は「元: 」に続けて並べ替えた後の data とします。
    • リストは Python が print する表記とします。「元:」の後の空白は半角1個とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    [48, 55, 70, 30, 10]
    [10, 70, 30, 55, 48]
    元: [10, 48, 70, 55, 30]
    
    ヒントを見る

    key に渡した関数は各要素に1回ずつ呼ばれ,要素そのものではなく,その戻り値どうしが比べられます。まず各要素の戻り値を紙に書き出してから並べてみましょう。

  8. 章末演習章末演習 8

    【複合】コラッツの操作で 1 になるまでの回数を返す関数を作ってください。この関数を max の key 引数に渡し,1〜10 のうち回数が最も多い数を求めます。

    入力

    入力なし(input は使わない)。調べる範囲は 1 以上 10 以下の整数とします。

    処理条件

    • 関数 collatz_steps(n) を def で定義します。n は 1 以上の整数とします。「偶数なら 2 で割る(//),奇数なら 3 倍して 1 を足す」を while 文で n が 1 になるまで繰り返し,その回数を整数で return します。n が 1 のときは 0 を返します。
    • 一覧を表示する前に,assert で collatz_steps(1) == 0,collatz_steps(6) == 8,collatz_steps(7) == 16 を確かめる。
    • 最多の数は max(range(1, 11), key=collatz_steps) の形で求めます。回数を別のリストに集めて添字で探す方法は使いません。

    出力

    • 11行を表示します。1〜10行目は n の小さい順に「n: 回数」とし,11行目は「最多: 数 (回数回)」とします。
    • 数字・コロン・丸かっこ・空白は半角,「最多」「回」は全角とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    1: 0
    2: 1
    3: 7
    4: 2
    5: 5
    6: 8
    7: 16
    8: 3
    9: 19
    10: 6
    最多: 9 (19回)
    
    ヒントを見る

    繰り返しの終了条件と,回数を増やす位置(操作の前か後か)を先に決めましょう。小さい n で手計算した回数と関数の戻り値を比べてから key に渡すと,誤りの原因を切り分けやすくなります。

  9. 章末演習章末演習 9

    【複合】関数を引数として受け取る二分法の関数を作り,x² − 2 = 0 の正の解(√2 の近似値)を求めてください。

    入力

    入力なし(input は使わない)。区間は lo = 1,hi = 2 とします。

    処理条件

    • 関数 bisection(f, lo, hi, tol=1e-6) を def で定義します。f(lo) と f(hi) の符号は異なるものとし,その確認は不要。
    • while 文で,区間の幅 hi − lo が tol より大きい間,中点 mid = (lo + hi) / 2 を求めます。f(lo) と f(mid) の符号から根を含む側の半分を選び,区間を縮める。繰り返しが終わったら,最後の区間の中点 (lo + hi) / 2 を return します。
    • x * x − 2 を返す関数 g(x) を def で定義し,bisection(g, 1, 2) と bisection(g, 1, 2, tol=0.01) を計算します。
    • 根の計算に math.sqrt,** 0.5,外部ライブラリは使いません。ただし答え合わせの assert では 2 ** 0.5 を使ってよいでしょう。

    出力

    • 2行を表示します。1行目は「√2 ≈ 値」とし,値は f 文字列の :.6f(小数第7位で丸めて第6位まで)で表示します。2行目は「tol=0.01 のとき 値」とし,値は :.2f(小数第3位で丸めて第2位まで)で表示します。
    • 「√」「≈」は実行例の記号をそのまま使い,前後の空白は半角1個とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    √2 ≈ 1.414214
    tol=0.01 のとき 1.42
    

    (tol=0.01 では最後の区間が [1.4140625, 1.421875] となり,その中点 1.41796875 を小数第2位までで表示するため 1.42 になります。)

    ヒントを見る

    二つの値の符号が異なるかどうかは,それらを掛けた結果の符号で判定できます。根が左半分にあるとき lo と hi のどちらを mid に置き換えるかを,数直線を描いて考えましょう。

  10. 章末演習章末演習 10

    【複合】条件を表す関数を差し替えて,点数の絞り込み・平均・表示を共通の関数で行ってください。

    入力

    入力なし(input は使わない)。プログラムの中に scores = [72, 45, 88, 60, 39, 95] を書きます。

    処理条件

    • select(values, keep):for 文で各要素に keep を呼び,True になった要素だけを元の順に集めた新しいリストを return します。filter と内包表記は使いません。
    • mean(values):平均値を float で return します。空リストなら None を return します。この関数では sum と len を使ってよいでしょう。
    • 条件関数 is_pass(x)(60以上で True),is_excellent(x)(80以上で True),is_perfect(x)(100 と等しいとき True)を def で定義します。
    • report(label, values, keep):select で絞り込み,結果を1行 print して,絞り込んだリストを return します。
    • report を「合格」「優秀」「満点」の順に3回呼ぶ。lambda は使いません。

    出力

    • 3行を表示します。各行は「ラベル: リスト 人数 人数 平均 平均値」の形とします。リストは Python が print する表記とし,平均値は丸めずに Python の既定の表示とする(該当者がいないときは None)。
    • 区切りは半角空白1個,コロンは半角とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    合格: [72, 88, 60, 95] 人数 4 平均 78.75
    優秀: [88, 95] 人数 2 平均 91.5
    満点: [] 人数 0 平均 None
    
    ヒントを見る

    一つの関数には一つの仕事だけを任せ,report は他の関数を組み合わせるだけにしましょう。該当者が0人のとき mean がどこで処理を終えるべきかを先に決めておけば,3行目も同じ report で表示できます。