プログラミング第9回 関数を定義する

 まずは前回の解答からです。問題1の解答です。

 次は問題2の解答です。

今回学ぶこと

・ 関数を定義して使う

・ 今まで作ったプログラムを関数を定義して見やすく変更する

・ じゃんけんゲームを作ってみよう

 関数を定義して使う

 「関数」というものはざっくり言うと、なでしこにやってもらう命令でしたね。今まで「表示」「言う」「整数変換」「代入」「二択」…など様々な関数を扱ってきました。今までの関数は初めからなでしこに設定されており、何の説明なしに関数を入力するだけで使うことができました。
 では、初めからなでしこに設定されていない命令(関数)を使いたい場合はどうすればいいか?
実は、自分で新たな関数を定義し設定することができるのです。今回は関数の定義の仕方を学習していきます。

 それでは、関数を定義するときの書式を見てみましょう。


●の記号は「まる」や「くろまる」と入力すれば変換候補に現れるはずです。
引数(ひきすう)というのは、関数に与える値のことです。
関数名は何でも好きなものでOKです。ただし、助詞が入ればエラーになることがあるので助詞や助詞っぽく見える名前は避けましょう。これは変数の名前と同じルールです(第3回参照)。


関数の定義の部分は、特殊変数「それ」を使っても大丈夫です。「それ」には直前の関数が代入されましたね。


「引数+助詞」の部分はいろいろアレンジできます。画像では(Nの)としてますが、(Nを)としても大丈夫です。助詞はわりと何でもいけます。
 ちなみに「二乗処理」を「2乗処理」とするとエラーになります。変数名と同様に頭に数字は付けれません(第3回参照)。


「引数+助詞」の部分はなくてもOKです。

 今まで作ったプログラムを関数を定義して見やすく変更する

 先ほど見たような短いプログラムならば、わざわざ関数を定義して使う必要はありません。関数を定義するメリットは長いプログラムのときに発揮されます。関数の定義し、見やすくすることによりエラーが起きにくくなります。

 それでは、第6回でやった「正多角形を描くプログラム」で関数を定義してみましょう。


これは関数の定義を使っていないバージョンです。プログラムが長くなってくると関数同士の関係も見にくくなります。


 関数を定義したバージョンです。「整数チェック」「図形描写」という2つの関数を定義しました。なでしこに命令したメインのプログラムはこの2つだけです。
 関数を定義したことにより、さらに高度なプログラムにしてあります。具体的には「3以上の自然数」を入力するまで繰り返し入力を求めてくるようにしました。
 「開始フラグ」という変数を設定し、「整数チェック」のときに「3以上の自然数」を入力した場合「開始フラグ=オン」となり、「整数チェック」から「図形描写」に移行します。「3以上の自然数」ではない数を入力した場合は「開始フラグ=オフ」のままなので「整数チェック」を繰り返します。
 そのため、「開始フラグ」という変数の初期設定は「開始フラグ=オフ」です。この変数の初期化(初期設定)は最初に行います。複数の関数で使われる変数は初期化しておく必要があります。
 「n」は尋ねた後に値が決まるので、適当な値で初期設定しておけば大丈夫です。ちなみに変数「m」も「m=0」などと初期化してもいいです。

 以上のように、関数を定義すると「変数の初期設定」「メインのプログラム」「関数の定義」がまとまって整理されるので、非常に見やすくなります。

  次は「2つのサイコロの目の和が奇数か偶数か当てるゲーム」を関数の定義を使って見やすくしてみましょう。


メインのプログラムは「サイコロゲーム実行」と「終了判定」です。「終了判定」で終了フラグがオンになるまで繰り返します。最初に変数を初期化することも忘れずに。
 一番最後の 「あなたの点数は{点数}点です」と言う はメインのプログラムに組み込んだ方がより見やすいですね。ちょっと失敗です。

 次は、「点数を賭けてサイコロの目を当てるゲーム」を関数の定義を使って見やすくしてみます。


 関数は「点賭ける」「目予測」「サイコロゲーム実行」「終了判定」の4つを定義しました。「点を賭ける」とか「目を予測」など助詞を入れないように注意してください。
 変数は最初にまとめて初期化(初期設定)しておきます。
 「サイコロゲーム実行」の結果から条件分岐し、点数が0点以下になれば「終了判定」をせずにゲームが終了(終了フラグがオン)。点数が0点より大きければ「終了判定」を行うようにしています。

 コピー&ペーストができるように下にプログラミングを記載しておきます。

賭点=0
予測=0
サイコロ=0
結果=0
点数=10
終了フラグ=オフ

「サイコロの目を当てるゲームです。目が当たると『大当たり』。賭けた点数が3倍になります。偶数か奇数かが当たれば『当たり』。賭けた点数がそのまま戻ってきます。はずした場合は賭けた点数を失います。」と言う

(終了フラグ=オフ)の間繰り返す
 点賭ける
 目予測
 サイコロゲーム実行
  もし、点数<=0ならば、
   「サイコロの目は{サイコロ}で{結果}です。点数が0点以下になりました」と言う
   終了フラグ=オン
  違えば、終了判定
ここまで

もし、点数<=0ならば、「ゲームオーバーです」と言う
違えば、「あなたの点数は{点数}点です」と言う

●点賭けるとは
 永遠の間繰り返す

 「あなたの点数は{点数}です。何点を賭けますか?」と尋ねて賭点に代入

  賭点を整数変換して値に代入

  もし、賭点<>値ならば、
   「点数は整数で入力してください」と言う
   続ける

  違えば、もし、賭点>点数ならば
   「持ち点を超えて賭けることはできません」と言う
   続ける

  違えば、もし、賭点<=0ならば
   「1以上の点数を賭けてください」と言う
   続ける

  違えば、抜ける

 ここまで

ここまで

●目予測とは
 永遠の間繰り返す
 「今から振るサイコロの目を予測してください。」と尋ねて予測に代入

  予測を整数変換して値2に代入

  もし、予測<>値2ならば、
   「整数を入力してください」と言う
   続ける

  違えば、もし、(予測<=0)||(予測>6)ならば
   「1から6までの整数を入力してください」と言う
   続ける

  違えば、抜ける

 ここまで

ここまで

●サイコロゲーム実行とは

 予測余り=予測%2

 サイコロ=乱数(6)+1
 サイコロ余り=サイコロ%2

  もし、予測=サイコロならば
   結果=「大当たり」
   点数=点数-賭点+賭点*3

  違えば、もし、予測余り=サイコロ余りならば
   結果=「当たり」
   点数=点数-賭点+賭点

  違えば、
   結果=「はずれ」
   点数=点数-賭点

  ここまで

ここまで

●終了判定とは
 「サイコロの目は{サイコロ}で{結果}です。点数は{点数}点です。続けますか?」と二択

  もし、それがキャンセルならば、終了フラグ=オン

ここまで

 関数を定義してじゃんけんゲームを作る

 次は、じゃんけんゲームを作ってみましょう。

じゃんけんの勝ち負けはどのように判定すればよいでしょうか?
✊(グー)を0、✌(チョキ)を1、✋(パー)を2としたときに、自分の手と相手の手のパターンを考えてみます。

(自分の手-相手の手)を計算したところ、
  0 ならば あいこ
  -1,2 ならば 自分の勝ち
  -2,1 ならば 相手の勝ち
となります。

 この後のオレンジの文字の部分は、高校数学の内容に触れますので中学生の方は読み飛ばしてもらっても大丈夫です。

 高校の数学Aで習う「整数(数学と人間の活動)」の知識を使えば規則性が見えます。
ある数を3で割った余りは、0,1,2のいずれかです。余りの周期性に着目すると、ある数を3で割った「余りが2」と「余りがー1」は同じものでしたね。ある数を3で割った「余りが1」と「余りがー2」も同じものになります。合同式を使うと、$2\equiv-1$ (mod 3),$1\equiv-2$ (mod 3) でしたね。

 つまり、じゃんけんの勝ち負けの判定は(自分の手-相手の手)を3で割った余りで分類できるわけですが、なでしこでは「余りが2」と「余りがー1」を同じものとして扱ってくれないので少し修正します。

 (自分の手-相手の手)+3 を3で割った余りを表で見てみます。

どうでしょうか?(自分の手-相手の手)+3 を3で割った余り が
 0 ならば あいこ
 2 ならば 自分の勝ち
 1 ならば 相手の勝ち
となっています。
この結果を使ってなでしこでプログラミングしていきます。


 これでじゃんけん自体は成立しており勝ち負けは表示されます。ただ、相手の出した手がわからないので「負け」と言われても何か納得ができないものがあります。
 そこで、自分の出した手と相手が出した手がわかるように改良します。


(番号の)手形」という関数を新たに定義しました。自分の手も相手の手も0か1か2の番号に変換されているので、(番号の)とすることにより自分の手の形も相手の手の形もまとめて表示されるようになります。
 これで自分と相手の手がわかるようになり、勝敗がよくわかるようになりました。
 これもコピー&ペーストができるように下に記載しておきます。

自手=0
値=0
相手=0
終了フラグ=オフ

(終了フラグ=オフ)の間繰り返す
手決める
じゃんけん判定
終了判定
ここまで

●手決めるとは

永遠の間繰り返す

「✊は0、✌は1、✋は2です。それでは、じゃんけんぽん!」と尋ねて自手に代入

自手を整数変換して値に代入

 もし、自手<>値ならば、
 「✊は0、✌は1、✋は2ですよ。」と言う
  続ける

 違えば、もし、(値<0)||(値>2)ならば
 「✊は0、✌は1、✋は2ですよ。」と言う
  続ける

 違えば、抜ける

 ここまで
ここまで

●じゃんけん判定

相手=乱数(3)
値=(自手-相手+3)%3

もし、値=0ならば、
  結果=「あいこ」

違えば、もし、値=2ならば
  結果=「あなたの勝ち」

違えば、
  結果=「あなたの負け」
ここまで

「あなたの手は{自手の手形}、私の手は{相手の手形}で{結果}です。」と言う

ここまで

●終了判定とは
「じゃんけんを続けますか?」と二択
もし、それがキャンセルならば、終了フラグ=オン
ここまで

●(番号の)手形とは
 もし、番号が0ならば、それは「✊」
 もし、番号が1ならば、それは「✌」
 もし、番号が2ならば、それは「✋」
ここまで

それでは今回の課題です。

問題1

前回の課題で作ったサイコロの目が「1~3」か「4~6」のいずれかを当てるゲームを関数の定義を使って見やすく改良してみましょう。

問題2

先ほどのじゃんけんゲームに点数をつけてみましょう。
変更するポイントは、最初に変数「点数」を初期化します。
次に、「●じゃんけん判定」のところで、「点数=点数±α」を使います。
さらに、「●終了判定とは」のところを、「あなたの点数は{点数}です。じゃんけんを続けますか?」と変更します。
あとはいろいろと自分なりにアレンジしてみましょう。