課題レポート2: 自動販売機の気持ちになろう。#

課題概要#

  • 自動販売機を実装してみよう。

  • 変数を用いて「ある状態」を保存し、管理できるようになろう。

  • 条件分岐を用いて実行するコードを切り替えられるようになろう。


取り組み方#

  • 友人らと話し合って取り組んで構わないが、考察は自分自身の言葉で述べること。試して分かったこと、自身で解決できなかった部分等についてどう取り組んだか、といった過程がわかるように示すこと。考えを図表や文章を駆使して表現して報告する練習です。

    • ある事象について妥当と思われる表現が思い浮かばないのなら、そのことを先輩らに尋ねてみよう。


レベル1「初期状態を設定せよ」#

  • 背景

    • 自動販売機で商品を購入するためにはお金を入れ、商品を選び、商品と釣り銭を受け取るという手続きを踏む必要がある。このやり取りにおいては釣り銭確認が必要となるため、予め自動販売機で扱うコイン種別と枚数を設定しておくことにしよう。ここでは簡易的な自動販売機として「110円の缶飲料、160円のペットボトル飲料」だけを扱うとしよう。これらの飲料は便宜上売り切れは発生しないものとする。またお金は10円玉、100円玉だけを扱うとしよう。

  • 課題

    • step 1: 10円玉、100円玉の枚数を保存するための変数名を検討せよ。

    • step 2: 自動販売機内には10円玉が3枚、100円玉が4枚あるとし、先程検討した変数に保存するコードを書け。なお後述する実行例のように関数を使うこと。(このように実行できる関数を実装すること)

  • レポートに含める内容

    • 書いたコード。

    • 動作を確認できる実行結果。VSCodeで実行したならば、VSCode内のターミナル画面のコピペで良い。

  • 実行例

    • あくまでも例 であるため、出力形式が多少違ってても処理結果を確認できるなら良いものとする。

# 必ずしもインタプリタ上で実行する必要はない。
# ここでは関数実行方法とその結果の確認方法を想像しやすくするため、
# init_vending_machine関数をインタプリタ上で実行する様子を示している。
# 第1引数は10円玉の枚数、第2引数は100円玉の枚数として設定した。
# 戻り値の1番目は10円玉の枚数、2番目は100円玉の枚数として設定した。
>>> coins10, coins100 = init_vending_machine(3, 4)
>>> print(coins10, coins100)
3 4

レベル2「入金処理せよ」#

  • 背景

    • 顧客が投入したコイン種別と枚数を正しくカウントし、合計金額を算出する必要がある。

  • 課題

    • ユーザが投入したコイン枚数を保存するコードと、投入された金額を算出するコードを書け。

      • 例1:100円玉が1枚投入された。合計100円。

      • 例2:100円玉1枚、10円玉1枚が投入された。合計110円。

      • 例3:100円玉が2枚投入された。合計200円。

  • 補足

    • この時点ではユーザが投入したコインを自動販売機内のコインに含めないようにしよう。(購入取り消しや金額不足時にそのまま返却するため)

  • レポートに含める内容

    • 書いたコード。

    • 動作を確認できる実行結果。VSCodeで実行したならば、VSCode内のターミナル画面のコピペで良い。

  • 実行例

    • あくまでも例 であるため、多少違ってても処理結果が正しければ良いものとする。

# 入金処理する関数 input_processを作成。
# 第1,第2引数を自動販売機が保有する10円玉の枚数、100円玉の枚数とした。
# 第3,第4引数を顧客が投入した10円玉の枚数、100円玉の枚数とした。
# 戻り値の1つ目・2つ目は自動販売機が保有する10円玉の枚数、100円玉の枚数とした。
# 戻り地の3つ目は顧客が投入した金額合計とした。
>>> #例1
>>> coins10, coins100 = init_vending_machine(3, 4)
>>> user_coins10, user_coins100, user_total = input_process(0, 1)
>>> print(user_coins10, user_coins100, user_total)
0 1 100
>>> #例2
>>> coins10, coins100 = init_vending_machine(3, 4)
>>> user_coins10, user_coins100, user_total = input_process(1, 1)
>>> print(user_coins10, user_coins100, user_total)
1 1 110
>>> #例3
>>> coins10, coins100 = init_vending_machine(3, 4)
>>> user_coins10, user_coins100, user_total = input_process(0, 2)
>>> print(user_coins10, user_coins100, user_total)
0 2 200

レベル3「購入可否を確認せよ」#

  • 背景

    • 選択した商品を購入した後で実は入金不足だったということが起きては駄目だ。そのため、(1) 入金合計金額が希望商品の金額を上回っていること、(2) 釣り銭が必要な場合には、釣り銭を準備できること、の2点を購入完了前に確認する必要がある。

  • 課題

    • 以下の状況について (a) 購入可否を判定すると共に、 (b) 釣り銭を算出するコードを書け。判定結果は bool型 とする。釣り銭はコイン種別ごとの枚数とする。

      • 例1:100円玉1枚が投入され、ペットボトル飲料が選択された状況。(購入不可、全額返金)

      • 例2:100円玉1枚、10円玉1枚が投入され、缶飲料が選択された状況。(購入可、釣り銭なし)

      • 例3:100円玉2枚が投入され、ペットボトル飲料が選択された状況。(購入不可、全額返金)

  • 補足

    • この時点では購入処理はせずとも良い。この判定結果を利用して後で処理することを想定している。

    • 合計金額だけではなく、釣り銭を返せるかも確認すること。釣り銭確認時には事前に自動販売機内で保有していたものだけから返却できるかを確認すること。顧客が多めに入力する場合のことは考慮しなくて良い。

    • 上記と相反するようだが、開発途中段階では条件を緩和して取り組むことを推奨する。例えば「合計金額だけで判定してみる」「釣り銭だけで確認してみる」「両方を組み合わせてみる」のように分解+組み合わせてみるという取り組み方だ。最終的には指定条件を満足するコードを仕上げてほしいが、後述する「小さく生んで大きく育てる」のためにもこのような取り組み方を推奨する。

  • レポートに含める内容

    • 書いたコード。

    • 動作を確認できる実行結果。VSCodeで実行したならば、VSCode内のターミナル画面のコピペで良い。

    • どのような過程を経てコードに翻訳したのか説明 すること。図等を用いても良い。

    • ソースコードは report2.py という名前で保存し、アップロードすること

  • 実行例

    • あくまでも例であるため、多少違ってても判定結果が正しければ良いものとする。

# 購入可否を確認する関数 can_buy を作成。
# 第1,2引数は自動販売機が元々保有する10円玉,100円玉の枚数。
# 第3,4引数は顧客が投入した10円玉,100円玉の枚数。
# 第5引数は投入金額。第6引数は選択した商品の金額。
# 戻り値の1番目は購入可否。2,3番目は返却する10円玉,100円玉の枚数。
>>> coins10, coins100 = init_vending_machine(3, 4)
>>> user_coins10, user_coins100, user_total = input_process(0, 1)
>>> is_possible, return_coins10, return_coins100 = can_buy(coins10, coins100, user_coins10, user_coins100, user_total, 160)
>>> print(is_possible, return_coins10, return_coins100)
False 0 1
>>> # 例2
>>> coins10, coins100 = init_vending_machine(3, 4)
>>> user_coins10, user_coins100, user_total = input_process(1, 1)
>>> is_possible, return_coins10, return_coins100 = can_buy(coins10, coins100, user_coins10, user_coins100, user_total, 110)
>>> print(is_possible, return_coins10, return_coins100)
True 0 0
>>> #例3
>>> coins10, coins100 = init_vending_machine(3, 4)
>>> user_coins10, user_coins100, user_total = input_process(0, 2)
>>> is_possible, return_coins10, return_coins100 = can_buy(coins10, coins100, user_coins10, user_coins100, user_total, 160)
>>> print(is_possible, return_coins10, return_coins100)
False 0 2

課題ではないが押さえておきたいポイント#

Note

「関数名・引数・戻り値」の組み合わせを API(Application Programming Interface) と呼ぶ。関数を使いたい人の立場からすると中身はわからなくても良く、APIが明確になって入れば十分である。このような関数利用者向け説明書のことを APIドキュメント と呼ぶ。例えばprint関数のこの文書はAPIドキュメントの例である。

Note

レベル1〜3のように具体的な中身の実装方法はブラックボックスとして後で考え、先にAPIを明確にしてから実装方法を検討する と自身がどこに理解不足があるのか、どこを整理できていないのかといった 問題をあぶり出しやすくなる

今回の例では自動販売機を実装することが本課題だったが、レベル1の背景文だけを読んだ状態であなたならどのようにAPI設計するだろうか。100%正しい回答があるわけではなく、一種の工芸品であるため「他に良い設計はないだろうか?」を模索し続けることが一つの勉強方法である。また良い設計を身につけるためにも他人が書いたコードを読んでみるのも推奨する。

Note

小さく生んで大きく育てる

最初から大きな成果でなくてよい。何度もバージョンアップすればよいのだから。 小さくてよいので具体的なアウトプットを生み出し、それを徐々に大きな成果に育てていく。

  • 自分がやりたいことを思い描き、まずは最小限の規模で、具体的なアウトプットを生み出す。大きな構想がある場合には、その構想の縮小版や一側面を体現したものでよい。そのアウトプットを、現段階の成果として発表する。

  • そのアウトプットを修正したり、新たに追加したりすることで、徐々に成長させていく。

by ラーニング・パターン No.24, 小さく生んで大きく育てる


オプション「購入処理を実行せよ」#

  • これはおまけ課題である。余裕がある人のみやってみよう。

  • 選択した商品が購入可能だった場合には購入処理し、釣り銭を返却すると共に購入金額を自動販売機内に保管しよう。投入額が不足している場合には投入額をそのまま返却しよう。