変わる替わる
こんばんは!
うつつのかぼすです。
明日から、いや昨日からでしょうかめまぐるしくかぼすの周り、かぼす自身が変化してきています。
それに、また決戦が待ち構えています。すぐ目の前に。
少なくとも決戦はあと3回あります。
すべて無事に乗り越えられればいいのですが。。。
自信はありません。
さて、今日は久々にアプリを作っていました。
とは言っても、結局はいろいろとロジックなどを考えているうちに時間が来てしまいました。
ああ、そういえば、明日も明日でとても忙しい一日なのです。
考えるだけで今から憂鬱です。
頑張ります。
では!
入門じゃないJava(9)
こんにちは!
今日も楽しくJavaを見ていきましょう。
前回は・・・配列ですね。
今日も配列です。
配列を逆順に並び替える
配列の全要素を逆順に並び替えるプログラムを考えてみましょう。
逆順に並び替える時にはどうすればよいのでしょうか?
例えば、7つの要素を持つ配列を逆順に並び替える手順は下のように考えられます。
まず、図aのように先頭要素と末尾要素の値を交換します。
次にb, cのようにそれぞれ、一つ内側の要素を交換する作業を繰り返します。
一般に要素数がnの時、交換回数はn / 2回です。ここでの剰余は切り捨てます。
それは、要素数が奇数のとき、中央の要素を交換する必要がないからです。
したがって、要素数がnである配列の要素の並びを反転するアルゴリズムの概略は、
for (int i = 0; i < n /2; i++)
//a[i] と a[n - i - 1]を交換する。
このアルゴリズムを使うと以下のようになります。
import java.util.Random; import java.util.Scanner; class ReverseArray { public static void main(String[] args) { Random rand = new Random(); Scanner stdIn = new Scanner(System.in); System.out.print("要素数>"); int n = stdIn.nextInt(); int [] a = new int[n]; for (int i = 0; i < n; i++) { a[i] = 10 + rand.nextInt(90); System.out.printf("a[%2d] = %2d\n", i, a[i]); } for (int i = 0; i < n / 2; i++) { int t = a[i]; a[i] = a[n - i -1]; a[n - i - 1] = t; } System.out.println("要素の並びを反転しました。"); for (int i = 0; i < n; i++) System.out.printf("a[%2d] = %2d\n", i, a[i]); } }
実行結果
配列が逆順になっていることがわかりますね。
今回はここまで!
では!
参考文献
柴田 望洋 (2016) .『新・明解Java入門』198-199
入門じゃないJava(8)
こんにちは。
今日もJavaを見てみましょう。
前回、前々回と配列を見てきました。
今回も配列です。といっても、今回で(今回こそ)一次元配列を終わらせて、次回からは多次元配列を見ていきましょう。
拡張for文
配列を扱う際には必ずと言っていいほどfor文を利用します。
このfor文を基本for文と呼びます。
もう一つの拡張for文を利用すると配列の走査をきわめて簡潔に実現で
きます。
これは配列の全要素の合計を求めて表示するプログラムです。
class ArraySumForIn { public static void main(String[] args) { double[] a = { 1.0, 2.0, 3.0, 4.0, 5.0 }; for (int i = 0; i < a.length; i++) System.out.printf("a[%d] = %.2f\n", i, a[i]); double sum = 0; for (double i : a) sum += i; System.out.printf("全体の和は%.2fです。\n", sum); } }
実行結果
新しい形式のfor文が出てきましたね。
()の画家のコロン:は"~の中の"という意味です。
そのため、この拡張for文は
このことから、拡張for文は"for-in文"あるいは"for-each文"などとも呼ばれます。
このfor文は以下のように理解します。
配列aの先頭から末尾までの全要素を1つずつ走査します。ループ本体では現在着目している要素をiと表現します。
すなわち、変数iはint型の整数値である"インデックス"を表すのではなく、double型の"走査において着目している要素"を表します。
拡張for文を利用するときには以下のようなメリットがあります。
配列の長さを調べる手間が省ける。
イテレータと同じ方法で走査を行える。
配列を逆順に並び替える
配列の全要素を逆順に並び替えるプログラムを考えてみましょう。
今日はここまでにしましょう。
では!
参考文献
柴田 望洋 (2016) .『新・明解Java入門』196-197
入門じゃないJava(7)
こんにちは。
今日もJavaを楽しくー見ていきましょう。
前回は配列を見ていきましたが、今日も配列を見ていきたいと思います。
できれば、今日で一次元配列を終わらせて、早く多次元配列に入りましょう。
棒グラフの表示
import java.util.Random; import java.util.Scanner; class IntArrayRand { public static void main(String[] args) { Random rand = new Random(); Scanner stdIn = new Scanner(System.in); System.out.print("配列要素数>"); int n = stdIn.nextInt(); int[] a = new int[n]; for(int i = 0; i < n; i++) a[i] = 1 + rand.nextInt(10); for(int i = 0; i < n; i++){ System.out.printf("a[%2d] : ", i); for(int j = 0; j < a[i]; j++) System.out.print("*"); System.out.println(); } } }
実行結果(一例)
上のプログラムを眺めながら前回までの流れを思い出してみましょう。
配列の初期化と代入
配列の全要素が既定値0で初期化されるとはいえ、個々の要素に入れるべき値があらかじめ分かっているのであれば、明示的に初期化を行うべきです。
class IntArrayInit { public static void main(String[] args) { int[] a = {1, 2, 3, 4, 5, 6, 7, 8}; for(int i = 0; i < a.length; i++) System.out.println("a[" + i + "] : " + a[i]); } }
配列に与える初期化子は、各要素に対する初期化子をコンマ, で区切って順に並べてそれを{ }で囲ったものです。生成される配列の要素数は初期化子の個数に基づいて自動的に決定されます。
本プログラムでは明示的にnewしていないにもかかわらず要素数8の配列が生成されます。
なお、以下は×
int a;
a = {1, 2, 3, 4, 5, 6, 7, 8};
正しくは下記の通り
int a;
a = new int{1, 2, 3, 4, 5, 6, 7, 8};
というのも、new演算子による配列生成時は"new 要素型"の後に初期化子をつけてもよいことになっているからです。
もう一度、正しくは、
要素型 変数名;
変数名 = new 要素型{要素, 要素, 要素};
という形式です。
配列要素への値の読込み
import java.util.Scanner; class ComingAggregate { public static void main(String[] args) { Scanner stdIn = new Scanner(System.in); final int BIZ_HOURS = 8; //時間 int sum = 0; //合計 int[] visitors = new int[BIZ_HOURS]; //来客数 System.out.println("時間ごとの来客数を入力してください(9時~17時)"); for(int i = 0; i < BIZ_HOURS; i++) { System.out.printf("%2d時~%2d時までの来客数>", i + 9, i + 10); visitors[i] = stdIn.nextInt(); sum += visitors[i]; } System.out.printf("総来客数は%d人です。\n", sum); System.out.printf("平均来客数は%.2f人です。\n", (double)sum / BIZ_HOURS); } }
実行結果
配列の要素の最大値を求める
import java.util.Scanner; class ComingMaxTime { public static void main(String[] args) { Scanner stdIn = new Scanner(System.in); final int BIZ_HOURS = 8; //時間 int sum = 0; //合計 int max = 0; //最大値 int[] visitors = new int[BIZ_HOURS]; //来客数 System.out.println("時間ごとの来客数を入力してください(AM10~PM6)"); for(int i = 0; i < BIZ_HOURS; i++) { System.out.printf("%2d時~%2d時までの来客数>", i + 9, i + 10); visitors[i] = stdIn.nextInt(); sum += visitors[i]; if(visitors[i] > max) max = visitors[i]; } System.out.printf("総来客数は%d人です。\n", sum); System.out.printf("平均来客数は%.2f人です。\n", (double)sum / BIZ_HOURS); System.out.printf("最大来客数は%d人です。\n", max); } }
実行結果
先ほどのコードをベースにしています。
また、肝心な処理が少々腫れぼったい感じもしますが、ふるまい的には大きくは変わりません。
線形探索
探索は配列の要素の先頭から順に走査することによって実現てきます。探すべきキー値と同じ値の要素に出会ったら探索に成功です。こては線形探索、または逐次探索と呼ばれるアルゴリズムです。
ここで、解説するよりも先にコードを見てみましょう。
import java.util.Random; import java.util.Scanner; class LinearSearch { public static void main(String[] args){ Random rand = new Random(); Scanner stdIn = new Scanner(System.in); final int n = 12; int[] a = new int[n]; for (int j = 0; j < n; j++) a[j] = 1 + rand.nextInt(10); System.out.print("配列内の全要素の値\n{ "); for (int j = 0; j < n; j++) System.out.print(a[j] + " "); System.out.println("}"); System.out.print("探す数値>"); int key = stdIn.nextInt(); int i; for (i = 0; i < n; i++) if (a[i] == key) break; if (i < n) System.out.printf("%dはa[%d]にあります。", key, i); else System.out.printf("%dはありません。", key); } }
実行結果(一例:探索成功)
実行結果(一例:探索失敗)
探索の失敗と成功はiとnの比較で判定しています。
では、そもそも、成功と失敗の判断基準は何でしょうか?
①探索すべきキー値が見つからず、末端を通り過ぎた(及び通り越しそうになった)。
②探索すべきキー値と等しい要素を見つけた。
①が成立した時は探索失敗、②が成立したときは探索成功を意味します。
すると、要素数nの配列aからkeyと同じ値を持つ要素を探索するプログラムは、
int i;
for (i = 0; i < n && a[i] != key; i++){
;
// i < nであれば探索成功、そうでなければ探索失敗
先ほどの通り①か②のどちらか一方でも成立した時、繰り返しは終了します。
変数iはfor文を抜けた後に判定を行うため、for文の中ではなく前に宣言します。
もしも、、、
iがnと等しい
探索失敗→すべての走査を終了しているため、探索失敗。
iがnより小さい
探索成功→走査中にa[i]とkeyの値が一致したため②が成立しforを抜けているため探索成功。
なお、これらはbreak文を利用して書き換えることができます。
int i;
for (i = 0; i < n; i++)
if (a[i] == key)
break;
// i < nであれば探索成功、そうでなければ探索失敗
もう一度上のプログラムを見てみるとあっ!
とひらめく個所もあるでしょう。
今日はこの辺で失礼します。
では!
参考文献
柴田 望洋 (2016) .『新・明解Java入門』186-195
入門じゃないJava(6)
こんにちは。
今日も楽しくJavaを見ていきましょう。
ちなみに、前回はエスケープシーケンスを学習しましたね。
今回は配列を見ていきたいと思います。
ちなみに、配列を使うメリットや特徴に関しては、柴田 望洋 (2016) .『新・明解Java入門』p178-179に詳しくまとまっているのでそちらを参照してください。
配列
配列変数の宣言
配列の宣言は、
① 型 変数名;
② 型 変数名;
のいずれかの形になります。
ちなみにint型を構成要素型とするような配列の宣言は、これに則って宣言すると、
① int a;
② int a;
となります。
一般的には①のように宣言するかと思います。
ちなみに、ここで作られる配列変数は配列の本体ではありません。
配列の本体は配列変数とは別に生成する必要があります。
たとえば、構成要素が7個の配列の本体を生成してみましょう。
new int[7]
生成した配列本体は配列変数と関連付けする必要があります。関連付けは以下に示す代入式によって行われます。
a = new int[5];
これで、配列本体が生成されるとともに、変数aがそれを参照するようになります。
なお、配列変数の宣言の初期化子に配列本体の生成式を埋め込むとプログラムはスッキリします。
int[] a = new int[7];
こうすると、生成された配列本体を参照するように変数aが初期化されます。
構成要素へのアクセス
配列本体内のここの構成要素のアクセス(読み書き)は以下のようにインデックスを[ ]の中に与えることによって実現します。
配列変数名 [ インデックス ]
インデックスは"先頭の構成要素から何個後ろの構成要素であるのか"を表すint型の整数値です。
このインデックスは0から構成要素数-1個までです。
構成要素数がn個の配列の構成要素はa[0], a[1], a[2], ・・・, a[n - 1]です。
配列aの構成要素はそれぞれがint型の変数になっています。そのため、配列内の個々の構成要素に対しては自由に値を代入したり取り出したり出来ます。
配列の特徴
まずは、配列を使ったプログラムを走らせてみましょう。
class IntArray01 { public static void main(String[] args) { int[] a = new int[7]; a[0] = 5; a[2] = 10; a[4] = 15; a[6] = a[0] * 4; System.out.println("a[" + 0 + "] = " + a[0] ); System.out.println("a[" + 1 + "] = " + a[1] ); System.out.println("a[" + 2 + "] = " + a[2] ); System.out.println("a[" + 3 + "] = " + a[3] ); System.out.println("a[" + 4 + "] = " + a[4] ); System.out.println("a[" + 5 + "] = " + a[5] ); System.out.println("a[" + 6 + "] = " + a[6] ); } }
a[6]はa[0] * 4を代入しています。a[0]を取り出したのち4を掛けた積をa[6]に代入しています。
さらに、実行結果を見てみると、値を代入していないa[1], a[3], a[5]の値が0になっていることに気が付きます。
というのも、配列の構成要素は自動的に0で初期化されるという規則があるためです。
構成要素が初期化される値のことを既定値と呼びます。各型によって既定値は異なります。"柴田 望洋 (2016) .『新・明解Java入門』p183 Table 6-2"に詳しくまとめられているのでご参照ください。
一般に構成要素型がTypeである配列のことを『Type型配列』または[Type型の配列]と呼びます。すると上の配列aは『int型配列』です。
要素数の取得
下記のプログラムをご覧ください。
class IntArray02 { public static void main(String[] args) { int[] a = new int[8]; for(int i = 0; i < a.length; i++) a[i] = i * 8; for(int i = 0; i < a.length; i++) System.out.println("a[" + i + "}= " + a[i]); } }
実行結果
a[0] = 0
a[1] = 8
a[2] = 16
a[3] = 24
a[4] = 32
a[5] = 40
a[6] = 48
a[7] = 56
C:\Java\06>_
ここで新しい形式の式が出てきましたね。
a.length
これは、配列の構成要素数を取得するための式です。なお、配列の要素数は長さとも呼ばれます。
配列変数名.length
と言い換えられますね。
配列の要素数を表すlengthは、int型ではなく、final int型です。そのため、lengthに対して値を代入することはできません。
今日はここまでにして続きは次回見ていきましょう。
それでは!
参考文献
柴田 望洋 (2016) .『新・明解Java入門』178-185
入門じゃないJava(5)
こんにちは。
今日も楽しくJavaを見ていきましょう。
前回は基本型の縮小変換と拡大変換についてみていきましたね。
今回はエスケープシーケンス(拡張表記)についてみていきたいと思います。
エスケープシーケンス(拡張表記)
エスケープシーケンスでよく知られているのは\nや\tあたりでしょうか?
当ブログでも度々出ていると思います。
一般に\(バックスラッシュ)を先頭にした文字列で、それが何か一つの機能として振る舞うようなものものを一般にエスケープシーケンス(拡張表記とも)と言います。
エスケープシーケンスは文字リテラル、文字列リテラル内で利用されます。
ここで、エスケープシーケンスの例を示します。
エスケープシーケンス | 意味 |
---|---|
¥b | バックスペース |
¥t | 水平タブ |
¥n | 改行 |
¥r | 復帰 |
¥f | 書式送り |
¥' | シングルクオーテーション |
¥" | ダブルクオーテーション |
¥¥ | ¥文字 |
¥ooo (oooは8進数) | 8進数でoooの値を持つ文字 |
ユニコードエスケープ | 意味 |
¥uhhhh (hhhhは16進数) | 16進数でhhhhの値を持つ文字 |
参考:柴田 望洋 (2016) .『新・明解Java入門』p170 Table 5-6
エスケープシーケンスを眺めていると一見複数の文字列を表すのではないかと思われるかもしれませんが、これはこのエスケープシーケンスのワンセットでカウントされるのであくまで単一の文字です。
\b・・・バックスペース(後退)
\bを出力すると、現表示位置がその行内での直前の位置に移動します。
現在位置とは、出力先がコンソール画面であれば、カーソル位置の事です。
現表示位置が行の先頭にあるときに後退を出力した結果は規定されていません。多くの環境では前の行(上の行)にはカーソルを戻せないからです。
\f・・・書式送り
\fを出力すると現表示位置が次の論理ページの先頭位置に移動します。
通常の環境では書式送りをコンソール画面へ出力しても何も起こりません。
プリンタへの出力において改ページを行う際に利用します。
\n・・・改行
改行\nを出力すると現表示位置が次の行の先頭に移動します。
\r・・・復帰
復帰\rを出力すると、、現表示位置がその行の先頭に移動します。
画面に復帰を出力すると表示済みの文字を書き換えることができます。
下に参考文献によるスマートな例を示します。
class CarriageReturn { public static void main(String[] args) { System.out.print("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); System.out.println("\r12345"); } }
実行結果
12345FGHIJKLMNOPQRSTUVWXYZ
C:\Java\04>_
なお、かぼす的ぐちゃぐちゃな例は以下になります。
import java.util.Random; class RoopProgress { public static void main(String[] args) { Random rnd = new Random(); System.out.println("━━━━処理Rを開始します。━━━━"); System.out.print(" 00%の処理が完了しました。"); for(int i = 0; i < 100; i++) { System.out.printf("\r %2d", i); } int n; //0から9999999までの乱数を発生させ、7が出現したらループを抜ける do { n = rnd.nextInt(9999999); }while(n != 7); System.out.print("\r100"); System.out.println(); } }
実行結果
━━━━処理Rを開始します。━━━━
100%の処理が完了しました。
C:\Java\04>_
おそらく今までは、
実行結果
━━━━処理Rを開始します。━━━━
00%の処理が完了しました。
01%の処理が完了しました。
02%の処理が完了しました。
03%の処理が完了しました。
・・・中略・・・
97%の処理が完了しました。
98%の処理が完了しました。
99%の処理が完了しました。
100%の処理が完了しました。
C:\Java\04>_
となっていたハズなのでこれだけでもとてもいい感じです。
\t・・・水平タブ
水平タブ\tを出力させると。現表示位置がその行における次の水平タブ位置に移動します。なお、現表示位置が、行における最後の水平タブ位置にある場合や、その位置を過ぎている場合の動作は規定されていません。
class HorizontalTab { public static void main(String[] args) { System.out.println("ABC\t123"); } }
実行結果
ABC 123
C:\Java\04>_
水平タブ位置はOSなどの環境に依存するため、実行によって表示されるABCと123の間の余白の幅は環境によって異なります。
ということで今日はここまでにしましょう。
参考文献
柴田 望洋 (2016) .『新・明解Java入門』170-171
入門じゃないJava(4)
おはようございます。
今日も楽しくJavaを見ていきましょう。
前回(昨日)は書き終わらず見苦しいところをお見せしてしまいすいませんでした。
加筆の方も終わりましたので、「なんだこれは」と思われた方は、最後の部分だけ今一度ご覧いただければと思います。
さて、今日は前回のキャストのお話を完結させようと思います。
まずは基本型の拡大変換と縮小変換についてみていきます。
基本型の拡大変換と縮小変換
以下の変換は、基本型の拡大変換と縮小変換は2段階で行われます。
byteからcharへの変換
まず最初に、byteは基本型への拡大変換によってint型へと変換され、その後、基本型の縮小変換によってintからcharへと変換されます。
strictfpとFP厳密式
浮動小数点数の内部は複雑であるため、すべての実行環境で浮動小数点数の演算結果がピッタリと一致するわけではありません。というもの、厳密にIEEE745形式で表せる値のみを利用するような演算を行おうとすると、ハードウェア資源を十分に活用できなくなり、演算に時間がかかってしまうからです。
たとえば、intelのCPUの場合、浮動小数点数は内部的に80ビットで演算をする仕様になっており、それを利用した高速な演算が行われます。
実行環境に依存することなく、同じ演算結果を得られるようにするには、特別な宣言が必要です。
クラス宣言(第8章)・インタフェース(第14章)・メソッド宣言(第7章)にstrictfpというキーワードを指定します。たとえば、クラス宣言に指定する場合は以下のようになります。
//クラスABC内のすべての浮動小数点式をFP厳密にする strictfp class ABC { //... }このように宣言されたクラス(やインタフェース)の中では、すべての浮動小数点式はFP厳密(FP-strict)となります。その場合、、実行環境に依存することなく、同一の演算結果が得られることが保障されます。
FP厳密の場合、floatからdoubleへの拡大変換では、変換前後の数値の大きさが変化することはありません。その一方で、FP厳密でなければ、変換後の数値の大きさに関する情報が欠落する可能性があります。
なお、クラス中の特定のメソッドのみをFP厳密にする必要があれば、そのメソッドにstrictfpをつけます。
//メソッドf内のすべての浮動小数点式をFP厳密にする。 strictfp void f() { //... }クラス・インタフェース・メソッドにstrictfpがつけられていなくても、0.0や3.14のような定数に限っては、必ずFP厳密になることが保障されています。
*
通常の計算ではFP厳密にする必要は、それほどありません。厳密にIEEE 754形式にのっとた演算を行う必然性があるときのみ、指定するといいでしょう。
柴田 望洋 (2016) .『新・明解Java入門』p167 Columu 5-8より
かぼす的解説
strictfpはストリクトエフピーと読みます。ちなみにIEEEはアイ トリプル イーと読むとかっちょいいでしょう。アイ イー イー イーと言ってるとアイーン!って言っちゃいそうですからね。
と、今日はここまでにしましょう。
次回は拡張表記について学んでみましょう。
では!
参考文献
柴田 望洋 (2016) .『新・明解Java入門』167-169