19.0409

fun / 関数

関数とは、
  いくつかのコードを、ひとまとめにして、
  関数の名前 だけ(正確には、引数 がありますが)で、
  そのいくつかのコード が、実行できる仕組み
と、言えるでしょう。

いままでも、使ってきた、
  println()関数
が、そうです。

私たちは、単に、()の中 に、何かの値 を入れて、
使っているだけですが、
実は、println()関数 は、
多くのコード を、()の中 の、値 を、使って、
実行しているわけです。

また、関数を、使うと、
プログラム全体のロジックが、簡潔になります。
その、println() でも、
この関数一つで、
  コンソールに出力することが、分かる
ので、そのほかのコードとの、関わり合い/関係 が、
つかみやすくなります。
もし、関数を、使わなかったら、
長い長いコード について、
考えるだけでも、ずいぶん負担が増えるでしょう。

ここでは、トランプ を、切って、
好きなカードを、「上から何枚目」
を決めて、そのカード を、取る 関数 を、
作って見ます。

そして、その関数 を使って、
コンピュータ と、勝負してみましょう。

IntelliJ が、使える方は、
  readLine()関数
を使ってた、コードにします。

TryKotlin の場合は、
  fun main(args: Array<String>) { // コード }
の方の、main()関数 で、実行してみます。

ここでは、「省略が得意なKotlin」 ですが、
まずは、「きっちり、全部を書く形」 を、身につけましょう。

その前に、まず、とても単純なコードで、
関数を理解しましょう。

ただ、それだと、関数 を、やや実感できないと思います。
普通に書けばいいじゃないかと思う人もいるでしょう。
それで、関数を使うと、
  コード全体が、すっきりする/理解しやすくなる
ことを、実感するために、やや複雑な関数を、作って見ましょう。
また、しつこいですが、
  println()
ような関数を使うと、楽して、自分のやりたいことが実現できます。
つまり、Kotlin の、関数のあつまり/ライブラリ を、
どれだけ知っているか自体が、
どれだけいろいろなプログラミングが出来るかに、
即、関わってくるでしょう。


1. とても簡単な関数を使って、関数 を、理解する

まずは、超簡単な、関数です。

右にある、本当の関数 は、こんな構文に基づいています。

関数ですよ easyFun(仮引数: 型): お返しする型 {
    お返しします 仮引数で処理するロジックを書いておく
}

それでは、もう少し詳しく、見ていきましょう。

fun
  「関数ですよ」 以上に、言うべきことは、ないと思います。
  無理やり考えたら、function/関数 の、fun です。

easyFun
  関数の名前 です。
  名前の規則ですが、そういえば、まだ紹介していませんでした。
    1. アルファベット(もちろん半角文字で)。
    2. _(アンダースコアは、OK)
      / 今試しに使ってみたが、文法エラーではないが、慣習に従わないと言ってきた。
      / でも、私は、見やすい時には、使っています。
    3. 数字は、最初には、使えない!
    4. 変数、関数名 は、小文字で始めるのが、慣習。
    5. class名 は、大文字で始めるのが、慣習。
    6. キーワード(予約語)は、使えない!
  最初は、
    最初に数字を使わない、アルファベット
  ぐらいの気持ちでいいと思います。
  TryKotlin でも、エラーを、見つけてくれるので。
  例えば、キーワード を、使ってしまった時など。
  class は、大文字だとかは、いろいろなコードを、見るうちに、
  なれると思います。(文法ではないので、エラーではないです)

(x: Int)
  ここでの、x: Int は、
    仮引数(かりひきすう)
  と、言います。
  この部分は、言ってみれば、
    関数の、設計図
  なので、実際の、変数ではないので、
    仮の、引数
      引数 と言うのは、関数の中/{ ここ } で、使う 変数 や 値
  です。
  また、: Int は、
    引数の型
  を、示しています。
  これは、変数 の時と、同じ書き方ですね。

: Int
  ここに書くのは、戻り値(返し値) の、型 です。
  私たち、プログラマー から、見たら、関数から、値が戻ってくるので、
    戻り値
  関数から見たら、値を、返すので、
    返し値
  と、言います。
  私は、「返し値」 の、方が、自然だと思うのですが、
  圧倒的に、「戻り値」 と、書かれる場合の方が多いです。
  プログラミングなんだから、「関数」 の方が、主役なのではないでしょうか?
  でも、多数決に合わせて、「戻り値」 も、使っていくつもりです。

return
  この return(リターン)キーワード の、後の、値 が、返されるわけです。
  (ややこしいですが、ここでの 値 は、class型も、あり得ます、、
   一つ一つ押さえていけば、文脈から判断できるようになります。)

x * x
  ここで、仮引数 を使った処理が書かれます。
  ただし、return しない、関数の場合もあります。
    / 「32. 関数 ...」 で、説明します。

では、今の話を踏まえて、今のコード を、考えてみましょう。
また、コンソールに、表示されるのは、何になるかも考えてください。
// EasyFunction.kt(簡単な関数)
 fun main() {
     val answer: Int = easyFun(3)
     println(answer)
 }
 fun easyFun(x: Int): Int { 
     return x * x		       
 }
まず、今説明した、関数が使われている場面は、
  easyFun(3)
になります。
そして、
  easyFun(3) 自体が、値
ということになります。
なぜなら、この場合、
  Int型数値 3 を、実引数(実際の引数)
として、
処理された結果、
  3 * 3
が、返されるからです。

そして、
  val answer: Int = easyFun(3)
は、全体として、
  val 変数: 変数の型 = 値
と、なっています。
つまり、
  変数の定義式
なのです。
ということは、
  関数で返された結果
は、
  answer に、代入される
ということになります。
よって、コンソール画面 に、
表示されるのは、こうなります


2. 実際に関数を使って、簡単なゲームをする

こんなゲームです。:
  これは、トランプ(ジョーカー1枚 で、53枚 とする)を、シャッフル して、
  上から、何枚目かを決めて、プレイヤー が、引いた数と、
  コンピューター(com)が、引いた数で、大きい方が、勝ち という、簡単なゲームです。
    / 本当は、上から何枚目を引いても、シャッフルしてあるので、勝つ確率は、変わらないはず。
    / ただ、人間の場合は、「自分には、どこに強いカードがあるか分かる」 と、
    / 多かれ、少なかれ、思っているので、場所を決めていますが、
    / コンピュータの場合は、常に、一番上を引いていただきます。
  まあ、じゃんけん、みたいなものでしょう。

時短のために、
  今まで学習していないところ
  大まかなロジック
以外は、省略させていただきます。
また、
  常に、プレイヤー が、先手です。
    / また、ロジックが増えて、コードが増えて、大変なので、許してくだされ。
  文字コード は、utf-8(Bom無し)です。
    / プログラミング に関しては、utf-8 が、ほぼ標準です。
    / ♠ などは、「スペード」など を、変換すれば、OKです。(utf-8)

これは、IntelliJ で、実行する場合です。
こんな画面が出ますので、マウスで、
「1~53 のうちどれかを、タイプしてください」 の、下に、カーソルを合わせて、
半角数字の、1~53 のどれかを、入力してください。
もし、入力間違いや、何も入力しないと、プレイヤー の点数は、0点 になって、
コンピューター(Com)の、勝ちになります。

また、TryKotlinで、実行する時のコードは、こちらになります。

その前に、このコードは、長くて見づらいので、
所々を、省略したコードを見て、ロジック/このコードの流れ を、考えてみましょう。

まず、IntelliJ を使う時のコードで、考えてみます。
(多分、コマンドプロンプト でも、実行できると思うが、
 TryKotlin でも、実行できるし、今の段階で、コマンドプロンプト
 を、使う意義が、?だと、私は思う。
 でも、実行速度は、多分早いだろう。
 でも、サポートするゆとりがありません。)

では、このコード を、上から見ていきます。

まず、 を、見てください。
ここでは、MutableListインスタンス を、作っています。
トランプの、カード を、文字列 にしています。
記号は、文字コードの utf-8 は、非常にたくさんの文字が登録されているので、
例えば、「すぺーど」 で、変換すると、簡単に、「♠」 に、変換できると思います。
また、こういった文字は、「テキストデータ」 と言いますが、
  非常に、軽い(データが小さい)
ことが、特徴です。
ですから、実際のアプリでも、
  画像を使わなくても、事足りる場面
では、この、「テキストデータ の、記号や絵」 を、使った方が、いいでしょう。
アプリの実行速度が上がるはずです。
例えば、「りんご」 なら、「🍎」 な、風にも、変換できます。
また、HTML などでは、「色まで付いて」 来ます。
Androidアプリ でも、サポートされているかもしれません。

また、このコードの書く場所ですが、
  スコープ(「31. スコープ」 で、学習します)
から、考えます。
簡単言うと、
  ...val x: Int = 123...{...{...{...x...}...}...}
の場合、
  x は、x を、指しています
が、
  ...y...{...{...{...val y = 456...}...}...}
においては、
  y は、y を、指していない
のです。
つまり、
  { 変数any }
での、any は、
  { … } の、外側 の部分は、スコープ/見える範囲 に、入らない
という事なのです。

そして、この、Mutableインスタンス、cardList は、
このコード全体で使う変数 なので、一番、外に、置かれるわけです。
この、
  一番外側に置かれる変数
のことを、
  グローバル変数
と、言います。また、「31. スコープ」 の、ところで、復習しましょう。


カードの点数、cardPower と、コンピュータのカード点数comCardPowerは、ここで、定義します。
また、この変数には、いろいろな 値 が、使われるので、val ではなく、var を、
使ってください。
たまに、変数 が、使えない!とかあったら、置く位置が悪いのでは?と、考えてみてください。
(たまに、型 を、省略しているときが、ありますが、「型推論」 してくださいね。)
(comCardPower は、0/Int型リテラル で、定義しているので、Int です。)

次のコードからが、
  プログラムのスタート地点
  main()関数
です。

println() を使って、簡単な説明をします。

val yourEnter: String? = readLine()
このコードの意味するところは、
  ユーザーが、コンソール画面で、入力した 値/文字 を、
  変数 yourEnter(あなたの入力、という意味) に、代入します
という事です。

この、
  readLine()関数
は、println() のように、Kotlinコード で、
  いつでも、どこでも、使える 関数
です。
この関数は、
  コンソール画面に、入力された文字(たとえ、半角の数字 でも)は、
  一律に、String型 つまり、、文字列 に、して、
  return します。
ただし、このコードを、見ると、
  yourEnter の、型が、String?
となっていますね。
  この、?
に関しては、「27. NullPointerException」 のところで、
学習しますが、
  これが付いていると
  null かもしれない
という事を、意味します。
  どうして、null(値 がない)かもしれない、なのかというと、
もし、
  コンソール画面で、何も入力せず、Enterキー、、
という場合があり得るからです。
当然、null になりますね。
ですから、コードの中でも、
  null の時 のための、コード を、書く
必要があります。(後述します)

③:
まず、
  yourCardPower を、0 で、初期化/定義 しています
が、これは、入力 が、エラーの場合は、あなたのカードの点数は、0点になる
ことを、意味しています(0 から、変更されないので)。
そして、次の、when式 で、
入力された、1~53 の、「文字列」 を、「数値/Int型」 に、
場合に応じて、変更しています。
そして、変更された 数値 が、MutableList である、cardList での、
  「上から何枚目」
を、決めるための、インデックス(数値 でなければいけない)になるわけです。

そして、when式での、else が、
  null/何も入力せずEnterキー および、 間違った入力54, あいうえお, 54/全角文字 など
  のための、対策
になります。
なぜなら、else なので、"1"~"53" 以外ですから。
そして、その時は、変数 number に、
  99
を、代入しますが、これは、いわゆる
  フラグ/flag/旗/目印
というものです。
しかし、今検索したら、true/false で、使うと書いてあったので、
少し、変則的な使い方かもしれません。
どういうことかというと、
  入力がエラーであった場合、「Int型の99」 と決めておくことによって、
  その場合に、実行するコードを、決定できる
こういう事なのですが、
具体的には、次の、if文
  if(number == 99) {
    println("error")
  }
となっているように、99 の時に、「error」 を、出力するコードを、
簡単に、書けますね。

この(↑)if文 には、else部分 が、あります。
つまり、エラー出なかった場合/きっちり入力された場合、chooseCard( )関数 に、よって、
  プレイヤー の、選んだカード を、決定
し、さらに、
  getCardPower()関数 によって、
  その カードの得点 が、決定
されるわけです。
(それぞれの関数のアクションは、後で、見てみましょう)


ここでも、同様に、コンピュータ(Com/com)の、
  カードの得点 を、決定
します。
ここでは、カード自体は、シャッフルされるので、
一番上を取ることにします。(確率的には、どれでも同じはずなので)


これで、プレイヤー と、Com の、得点が、決まりました。
そして、when式 を使って、
  プレイヤーの得点 > Comの得点 ➡ "You won"(あなたが、勝ちました)
  Comの得点 > プレイヤーの得点 ➡ "Com won"(コンピュータが、勝ちました)
  else(つまり、同じ得点)➡ "Draw"(引き分け)
を、決定し、変数 out に、代入、println()で、コンソールに表示、、というわけです。

main()関数を、見ると、
それなりの、コードの流れ/ロジック があります。
ここで、使われている、関数
  chooseCard( )関数
  getCardPower()関数
を、関数にせず、コードで書いていたら、さらに、ロジックが、複雑になります。
また、私は、最初、getCardPower()関数 の部分を、
関数を使わずに、main()関数 の中に、
書いていました。
すると、同じような、長いコードの塊が、2つあることに気づきました。
それで、この部分も、getCardPower()関数 にしたのですが、
こうすると、ずいぶんコードが、短くなり、しかも、可読性が高まります。
(ここを見るだけで、カードを、得点に換算していることがすぐ分かります)
  ロジックが、分かりやすくなる
  同じようなコード を、まとめると、コードの量が、ずいぶん減る(分かりやすい)
    / 長いコードでも、まとめると、簡単に、しかも、「何か所でも」 使えます
こんなことが、関数 を使う利点だと言えますね。

では、最後に、
  chooseCard( )関数
  getCardPower()関数
について、見ていきましょう。

chooseCard( )関数:
  最初に作った、MutableListインスタンス、、cardsList
  を、シャッフル/shuffled() しています。
  (shuffled() は、MutableListインスタンス の、メソッド(あるインスタンス特有の関数) です)
    (メソッド については、後々、学習します)
  shuffled() を、使った場合、必ず、MutableListインスタンス でなくて
  Listインスタンス でしか、返せませんでした
  (違いは、要素を、変更できるかどうか)
  それから、シャッフルされた内容を、コンソールに、表示しています。
    / println(shuffledCard)
  また、この、chooseCard( )関数 の、実引数 に、注目してみましょう。
  仮引数 は、num: Int と、なっていますが
  実際には、 の中での、
    val yourNum = chooseCard(number)
  から、分かるように、
    number
  つまり、あなたが、入力した、数 の、Int型 に、なります。
  そして、Listインスタンス の、インデックス は、0 スタートでした。
  よって、プレイヤー が、引いたカードの、インデックスナンバーは、
  number -1 の、はずですね。
  そして、
    val selectedCard: String = shuffledCard[num - 1]
      / num - 1 の、実際は、number - 1
  から、分かるように、ユーザーが引いたカードは、
    String型 の、selectedCard
  という事になります。
  それから、それから、
   の部分の、
    yourCardPower = getCardPower(yourNum)
      / yourNum は、実際には、selectedCard.
  で、分かるように、プレーヤー が引いた、カード(String型) を、
  getCardPower()関数 に、渡しています。
また、次に、コンピュータ の、アクションのために、
    cardsList = shuffledCard.toMutableList()
    cardsList.remove(selectedCard)
  toMutableList() を、使って、再び、cardsList を、
  Listインスタンス ではなく、MutableListインスタンス に、戻しています。
  そうしないと、今ユーザーが、引いたカードを、削除できないからです。
  そして、ユーザーが、引いたカード を、削除します。

getCardPower()関数:
  この関数では、何をやっているのでしょうか?
  実際には、長いコードですが、
  やっていることは、簡単です。
  仮引数 の、whichCard: String は、
  これも、③ と ④ を、見れば分かりますが、
  実際には、
    yourNum(String型)
  と
    comNum(String型)
  です。
  そして、そのカード を、得点(Int型、cardPower) に、変換しています。
  変換のルールは、簡単で、
    カードの、数字 を、そのまま、Int型 の、数値 にする
    ジョーカー は、14(点) に、する
  これだけです。
  最後に、Int型 の、cardPower を、return します。

以上で、このコードの説明は、終わりです。
以前学習したことを、完全に覚えていないと、難しいと思います。
ぜひ、復習してください。



まとめ
このコードが、理解できますか?
このコードには、関数の、基本形、使い方 が、詰まっています。



TryKotlin を、使ったコードでの違い

一言でいえば、
  fun main(args: Array<String>) { // コード }
の方の、main()関数 を、使って、
  args[0]
  に、上から何枚目のカードを引くのか、を、、
  (実は、)文字列 で、渡す
実は、とは言わなくても、
  Array<String>
だと、言っているのだから、言う必要はないかもしれませんが、
  実際には、
    プレイヤー/ユーザー にとって分かりやすい
    半角数字 を、使う
と、普通は、思われますので、
  それを使うと、Int型 と、勘違いしやすいので、
  実は、文字列
だと、言ったのでした。

もう一つ、このコードでやる場合の違いがあります。
それは、
  ArrayIndexOutOfBoundsException 対策
です。

その前に、実際に、TryKotlin で、実行してみましょう。
このコード を、TryKotlin に、貼り付けてみてください。
(Ctr + A で、全選択、Ctr + C で、コピーできます。)
次に、TryKotlin の、この部分の、
  上にある 「Arguments」 の、部分を、クリックして、
  下に現れた、ウィンドウ に、1~53 の、半角数字 を、入力してください
その 数字が、
  上から何枚目を引くか
という事になります。
  後は、右上の、[Runボタン] を、クリックするだけです。

それでは、TryKotlin での、違いを、見ていきましょう。
( println("~") などの、細かい違いは、省略します )

1.
最初に言いましたが、
  fun main()
の部分を、
  fun main(args: Array<String>)
に、変更しましょう。
どうして、こちらを使うかというと、
  args
という、
  String型/文字列 が、要素になる、配列インスタンス
の、
  最初の要素
    どう表現するんでした?、、そうです、
    args[0] でしたね。
を、
  プレイヤー が、「上から何枚目を引くのか?」
に、答えるために、使うのです。

2.
あとは、
  ArrayIndexOutOfBoundsException 対策
ですね。
この部分に関しては、「18. 実行時例外」 で、やりますが、
簡単に言うと、
  例外(エラーの一種)が、起こる可能性があるところを、
  tryブロック / try{~} に入れて、実行してみる。
そして、
  catchブロック / catch{~} で、文字通り、例外オブジェクトを、キャッチして、
  その場合の処理を、catchブロック内 に、書く
という事なのですが、
先ほどのコードを、色分けして、スコーピング しましたので、確認してください。

こんなところです。
これは、次の、「実行時例外」 の、ところで、もう少し詳しくやりましょう。