質問: {3,4,9}; System.out.println("1行の数字を入力してください"); int input=new java.util.Scanner(System.in).nextInt(); for(int a:numbers) { if(a==input) { System.out.println("アタリ!"); } if(a!=input) { System.out.println("ハズレ"); } } } } ``` こちらが私が書いたコードなのですが、これだと「9」と入力した場合、 ハズレ ハズレ アタリ! と表示されてしまいます。 どのように改善すればアタリ!とだけ表示されるのでしょうか? 初歩的な質問だとは思いますが、どなたか教えていただけると助かります。 宜しくお願い致します。
回答: ### アルゴリズムについての整理 int型配列numbersにint型値inputが含まれていればアタリを表示し含まれていなければハズレを出力する。この要件を満たすような手続きの記述方法はいくつかある。 (紙面節約のためjavaらしくない{}の配置をしているがうまく読み替えてください) ### プログラム1 プログラム1は、上記アルゴリズムを直接書き下す方法でありおそらく速度も速い。 下記の方法ではbreakを使っており、アタリを見つけ次第forを抜けるため効率的である。 このようにアルゴリズムを直接書き下すようなやり方を命令的プログラミングと表現することがある。 ``` class Aaaa{ public static void main (String[] args) { int[] numbers = {3,4,9}; int input = new java.util.Scanner(System.in).nextInt(); for(int i=0;i ### プログラム2 プログラム1は動作は速いがいかにも不安が付きまとう。インデックスの指定が間違っているのではないか、バグが混入しているのではないか? 我々はもっと見通しのよい、バグのないことを確証できるようなプログラムを書きたいと思う。 本当はpythonやSQLでいうところのin演算子があればよいのだが、javaにはない。 ``` Arrays.asList(numbers).contains(input) ``` 本当は上記のように判定できたらいいのだが、javaはint型の配列(正確にはprimitive型の配列)をListにうまくキャストしてくれない。(上記の配列を要素として持つ長さ1のListになるため、この記述では期待する結果を得ることができない) 参考になるURLをもう一つ記述する。 この質問と回答は非常にレベルが高いが要約すると、 ``` class Aaaa{ public static void main (String[] args){ int[] numbers = {3,4,9}; int input = new java.util.Scanner(System.in).nextInt(); if(java.util.stream.IntStream.of(numbers).anyMatch(a -> a == input)) {System.out.println("アタリ");} else {System.out.println("ハズレ");} } ``` と記述するのがいいと示唆している。特に重要なのがif文の中である。 まずはint型配列numbersをint型のストリームにキャストしている。ストリームは配列などを頭から順番に1つずつ要素を取り出していくようなものだとイメージしておけばよい。 a -> a==inputはいわゆるラムダ式と呼ばれているもので、これは入力はa 出力はa==inputというbool型数値となるような関数を短く書いているに過ぎない。aの具体的な値は3とか4とか9が順番に入っていくとイメージすればいい。 上記ラムダ式(関数)は入力3,4,9というストリームをFalse,False,Trueというストリームに置きかえる。 anyMatch関数はFalse,False,Trueという流れ(ストリーム)をTrueに置き換える。 anyMatchは例えばTrue,False,Falseという流れを受け取った場合、最初がTrueだと分かった時点で計算をbreakしてTrueを返す、そういう関数であるので効率はそれほど悪くない。 プログラム2のような書き方を宣言的なプログラムと呼ぶことがある。アルゴリズムの詳細(インデックスがどうとか、分岐がどうとか)をプログラマが指定することなくただnumbersの中にinputと==になるものがあれば"アタリ"、そうでないならば"ハズレ"と出力せよと書いている。 ### どちらがよいか 基本的にはプログラム2のような書き方を推奨する。ストリームAPIは汎用性が高いプログラムを短く記述でき細かい分岐の指定を回避することができるためである。ただプログラム1のような記述を知っておくことも重要である。