最終更新日 |
|
|
文科系の学生に、前期、ホームページ作成をしてもらった後、Javaのプログラムに挑戦してもらうことにした。Excelなどは市販の本もたくさんあるのだから、基礎ができていれば、それを参考に試行錯誤すれば十分、もっとも基本的なことをやっておくのがいいだろうと思うから。Javaについては、すでに、基礎、基礎2,基礎3、フラクタル図形などのページを作っているが、やや、個人的興味からはいっているので、学生に配布予定の資料を基にして、その前段の部分を作ることにした。(5章以降はJava入門2 へ)
[このページ]
1 Javaはオブジェクト指向言語 2 操作、基本データ型、Integerクラス 3 if文と繰り返し処理
4 配列と繰り返し E [参考]エラーメッセージ例 S [参考] コマンドプロンプトの操作など
[Java入門2]
5 2進、16進数、API、クラスとイスタンス 6 文字列、Stringクラス
Java、学生向けのトップページ例(学生向けにまとめたページ(暫定版) と、そこからもリンクしている
Javaのプログラミング1ーApplication がある。
[2006/11/29 追加・修正]
4-3 素数 修正。 1も素数としていたが、削除。最初から偶数を除いてチェックするように変更。
4-4-2 ソート追加、「もっと詳しい途中経緯」。
4-5 ソート追加、配列の最大値を求め、それを配列右側と置き換える。
4-6 ソート追加、「大きい順にソート」 と 「4-5-2 大きい順にソート その2」。
Javaは、3通りあるが、ここでは、J2SE - Java 2 Platform, Standard Edition - デスクトップ・アプリケーション向け を取り上げる。 J2SEにも、 アブリケーションソフト を作成するもの、インターネット上で動くアプレット とがあり、まずは、アブリケーション版から入り、短いプログラムで、操作とプログラムの基本になれてもらう。その後、アプレット版でグラフィックスを中心にやっていきたい。そして、メニューやボタンなどのGUI(Graphic User Interface)もつけて、ユーザとの対話も図れる所までやってみたい。半年もなくて、無理は承知だが、今後の学習のきっかけになればよしとする。作品の一部は、ホームページにとりこんでもらう予定(上記、Java、学生向けのトップページ例を参考にして)。
最近の言語は、みな、オブジェクト指向 ということで、オブジェクト、クラス、インスタンス、コンストラクタ、メソッドとか、いろいろな用語が出てくるが、例題の中で説明していくことにして、まずは、基本データ型の変数、配列と繰り返し、if などプログラムの基本を重点にしながら進める。もっとも、Javaは典型的なオブジェクト指向言語とあって、クラス、インスタンス、メソッドといったことも最初から使う必要があるが、それも繰り返しやっていって、なれていただこう。
2-1 Javaのシステム
Googleにて、Javaで検索して、サンマイクロシステムズの日本語版ホームページ(Javaテクノロジ)から、Java2SE5.0(Java 2 SDK, Standard Edition 5.0 をダウンロード、インストール。(2005/10時点での最新版)。
(しばらく前までは、Java2SE1.4(Java 2 SDK, Standard Edition 1.4.2_06 であり、1.5の時点で、大幅に変わったということで、一気に5.0となったようである)。
2-2 環境変数の設定
コントロール・パネル-システム-詳細設定-環境変数で、ユーザとシステムの環境変数のPathに、c:\jdk1.5.0_05\bin を加える( Javaの基礎 の図を参照)。再起動後に有効となる。(内部的にはシステム名はまだjdk1.5のままのようである。)
3-1 プログラム(ソース)の作成
xyzzyや秀丸などのエディタソフト でおこない、拡張子はjavaとして保存。プログラム名とファイル名は同じにする。たとえば、PrintDemo.java。
3-2 コンパイルと実行
コンパイルと実行は、コマンドプロンプト で行う。
スタートープログラムーアクセサリからコマンドプロンプトをショートカットアイコンにしておく。
コマンドプロンプトのcd というコマンドをって、PrintDemo.javaというソースファイルのあるディレクトリ(フォルダ)に移動して、Javaのシステムにあるjavac.exeでコンパイルして機械語(バイトコード)にする。
javac PrintDemo.java 拡張子classのファイルができる。これはどのOSでも使えるように設計されている。ただし、OSごとにバイトコードを解釈、実行する実行ソフトが必要になる。これがJVM(Java Virtual Machine)。
この中にあるjava.exeを使って、java PrintDemo として実行。結果もコマンドプロンプトに出る。
なお、2-2の環境設定は、javacやjavaの起動を簡単に記述できるようにするためである。
4-1 API
クラスライブラリ概要(ネット上のJava2 SE5.0 API仕様 日本語版
(以前のものに、JavaTM 2 Platform, Standard Edition, v 1.4.2 API仕様
とか、さらに少し古い版のものには、日本語版もある。JavaTM Platform, Standard Edition, v 1.3API 仕様)
4-2 本
「独習Java」(ジョセフ・オニール、翔泳社 1999/5 初版)
「Javaチュートリアル 第3版」 M.カンピオーネ他 (ピアソン・エデュケーション 2001/11)
「コアJava2 Vol1.1基礎編」改定版(C.Horstmann/G.Cornell共著 ASCII 2001/12)
4-3 ネット上のもの
・オブジェクト指向
・オブジェクト指向 WITH JAVA
・初心者のためのJava講座
・Java講座
・Java
・プログラミング言語II (理系授業の教材のよう)
・4.データ型 (上記の一部)
・JavaFAQ: Java FAQ それ自身に関するよくある質問と回答
・Java VM Selector for IE v0.91
・The Source for Developers Search Results
・Java Plug-in Software HTML Converter Download
・GUI コンポーネントの作成 (1)
・Spinner
最も基本的なアブリケーションプログラムの作成(エディタ)とコンパイル、実行(コマンドプロンプト)の方法を述べる。数値(整数、実数)の基本データ型や足し算、掛け算、余りなどの演算といった基本から、String(文字列)とIntegerクラスと多少、オブジェクトにもふれていく。逐一の理屈はあと回しにして、例題を数こなして、こう書くもの、こう操作していくものという定石になれていくことが先決。
ソースプログラム作成と保存
・Zドライブに、JavaTryというフォルダ、その中に、Application1というフォルダを作っておこう。
・下記をxyzzyで打って、PrintDemo.java というファイル名で、 z:\JavaTry\Application1 に保存する。
下記のクラス名とファイル名は一致させる必要がある。
・最初の4行、//つきのものは、Javaに対するコメントであるが、コンパイルや実行のとき、
コマンドプロンプトで使うもの。こうしておくと、コピーー貼り付けで簡単になる。
//cd z:\JavaTry\Application1 (注 通常はcまたはdドライブなので、zはcまたはd)
//set CLASSPATH=.
//javac PrintDemo.java
//java PrintDemo
class PrintDemo {
public static void main(String[] args) {
System.out.println("First Java Application");
}
}
コマンドプロンプトを起動、コンパイルと実行
・cd z:\JavaTry\Application1をxyzzyからコピーする(Ctrl+Insert)。
コマンドプロンプト上で、右クリックして貼り付けとする(他の方法はない)。
(cd は change directory の意味で、javaのプログラムのあるディレクトリに移動。
・javac PrintDemo.javaを同様にして、コマンドプロンプト上にはりつけてEnterキー
(コンパイルして、PrintDemo.class を作成するため)。
・タイプミス、文法ミスがあれば、エラーが出るだろう。修正、上書き保存して再度コンパイル。
このときは、コマンドプロンプト上で上向きの方向キーまたは、F3のキーだけでよい。
両者とも、コマンド複写の働きをする。詳しくは、下記、[参考]に。
・コンパイルが無事できれば、Application1のフォルダに、PrintDemo.classができていることを確かめよ。
・java PrintDemo で実行(コンバイルずみのclassファイルをJVMに解釈させる)。
[実行結果] 画面に、First Java Application と 出るだけである。
[参考]
cdコマンドやjavacで出るエラーメッセージのみかたについては、E [参考]エラーメッセージ例 を参照のこと。
コマンドプロンプト画面での簡便操作法などは、S [参考] コマンドプロンプトの操作など を参照ください。
下記を打つのだが、2-1 をそっくり名前をつけて保存(ファイル名はPai.java )として
修正していくのがよいだろう。
class Pai {
public static void main(String[] args) {
System.out.println("PAI = " + Math.PI);
//半径rの円の面積
double r = 100.0;
double area= Math.PI * r * r;
double enshu= 2.0 * Math.PI * r;
System.out.println("半径100の円の面積 = " + area);
System.out.println("半径100の円周 = " + enshu);
}
}
[実行結果]
PAI = 3.141592653589793
半径100の円の面積 = 31415.926535897932
半径100の円周 = 628.3185307179587
[コメント]
・mainのある行は複雑であるが、あとで説明する。前の例題でも同じ記述だった。
しばらくはこう書く約束ということでいこう。
・ = は代入であり、数学でいう等号ではない(if文の中で使う等号には==を使う)。
左辺はメモリのある領域につけられた名前 で、そこに右辺の値ないしは処理結果を代入する。
・型名に必要な領域が確保される(この場合の型名はdoubleで64ビットの領域がとられる。3-3[参考] を参照)。
・*は掛け算(どのような演算記号があるか、2-2[参考] を参照)。
・+は数値同士なら足し算であるが、文字列の結合にも使われる。
print文の中では、本来数値であったものも暗黙のうちに文字列に変えられている。
・Math.PIは、数学的なMathクラス に用意されているクラス変数。
[問題]文字列として与えた"50"を整数に変換して、100を足す。
class StringDemo {
public static void main(String[] args) {
String s = "50";
System.out.println("s = " + s);
int n = Integer.parseInt(s);
int n100 = n + 100;
System.out.println("s + 100 = " + n100);
}
}
[実行結果]
s = 50 s + 100 = 150
[コメント]
int s = 50; なら、簡単な足し算だが、練習のため、わざわざ、50という数値に""をつけて、String型に
している。それに100を足したいが、数値にしないとできないので、IntegerクラスのparseInt(s)メソッド
を利用している(1-4.1のAPI、Integerクラスのメソッド参照)。次の例題への伏線である。
[問題]コマンドライン引数で与えた数に100を足す。
class ArgDemo {
public static void main(String[] args) {
System.out.println("Arg = " + args[0]);
int n = Integer.parseInt(args[0]);
int n100 = n + 100;
System.out.println("Arg + 100 = " + n100);
}
}
[実行結果]
java ArgDemo 50
Arg = 50 Arg + 100 = 150
[コメント]
・実行時に'java ArgDemo 50'という形で与える、この例では50をコマンドライン引数 という。
・4-3で後述するように、これを受け取るのが String[] args であり、引数が1ケの場 合はargs[0]である。
・コマンドライン引数は、数値で与えたつもりでも、コンピュータ内部で文字列に変えられているので、
int n = Integer.parseInt(args[0]); で数値に変換する(前の例題に同じ)。
コンピュータの最大の特徴は、何万回、何億回もの繰り返し処理をあきもせず、高速で処理してくれるところにあるだろう。同じことをしてもしかたがないから、変数ないし配列で値をかえながら....。場合によっては、条件が満たされたから、処理を打ち切場合もあるから、if文やswitch文による条件分岐も必要になる。
[問題]コマンドライン引数で与えた数が奇数か偶数か?を判定する。2で割ったあまり(%)が0なら偶数。
(「独習Java」にあった例題)
class Even {
public static void main(String[] args) {
String s= args[0];
int n=Integer.valueOf(s).intValue();
if ((n % 2) == 0) {
System.out.println(n + " = odd");
} else {
System.out.println(n + " = even");
}
}
}
[実行結果]
java Even 16
16 = odd
java Even 11
11 = even
[コメント]
・条件による分岐、if(...){..... }; ないし、
if(...){.....} else(...){.....};という記述法に注意。
・{.....}と括弧でくるまれる部分をブロックという。
・if文の中の % は余りを求めるもの、== に注意。= を代入の意味で使うので、ほんとうに等しいことを
問うのは、==。
・コマンドライン引数(文字列)を数値に直すのを2-4のparseInt(s)ではなく、別法を使った。
valueOfでIntegerオブジェクトとし、それをintValueで整数にする。
int n = Integer.valueOf(args[0]).intValue();
・整数はintだが、それをオブジェクトにするのがIntegerクラス(API参照)。
・オブジェクトにすると、そのメソッドを使って型変換できたり、なにかと便利。
・if elseブロックの省略した記述がある。三項演算子という。下記。
[別解]
class Even {
public static void main(String[] args) {
int n=Integer.parseInt(args[0]);
System.out.print(n + " = ");
System.out.println((n % 2) == 0 ? "odd" : "even");
}
}
最後の行が三項演算子 といわれる、if文の省略形。
[参考]
余り(remainder)を求める%が出てきたところで、Javaに、どのような演算(Operators)があるかを
まとめたプログラムを紹介する。
サンマイクロ社の「Javaチュートリアル 第3版」に紹介があり、ネット上にある、Operators である。
なお、そのプログラムの演算記号一覧ににない 巾乗(a^b)には、Math.pow(double a,double b) を使う。
コンピュータの最大の特徴である繰り返し処理。forとかwhile,do、再帰用法と、いろいろあるが、ここではforループを扱う。
1から10までの和を求める例。
class Repeat {
public static void main(String[] args) {
int total = 0;
for(int i = 0; i <= 10; i++) {
total = total + i;
}
System.out.println("1から10までの和 = " + total);
}
}
[実行結果] 1 から10までの和 = 55
[コメント]
1 for (i=1; i<=n; i++) は、カウンタとして使う整数iの初期値、条件判定、iの足しこみで、
条件が満たされている間は、ブロック({ ...}をいう)内の処理を繰り返す。
2 変数には、型宣言が必要である。この例題のtotal,iはともに整数型、型名はint(、32ビット)である。
2-2のdoubleは倍精度小数点つきであった。
3 型宣言はメソッドにも必要で、これまでの例題では、必ず、mainというメソッド(他の言語の関数相当)
があるが、処理内容はprint文で画面表示するだけで、戻り値がないため、voidとしている。
それがこの場合のmainメソッドの型名である。
4 forを使った繰り返し処理の動きをはっきりさせるために、printlnないし、改行しないprint文
を調べたいところにいれて、結果を見ていくという手段がある(実例、下記)。
実行結果がおかしいときのプログラム・デバッグ(エラーつぶし)にもよく使う。
print文は結果を表示するだけに使うわけではない。
class Repeat1 {
public static void main(String[] args) {
int total = 0;
//1から10までの和
for(int i = 1; i <= 10; i++) {
total = total + i;
System.out.print("i = " + i);
System.out.println(" total = " + total);
}
System.out.println("total = " + total);
}
}
[実行結果]
i = 1 total = 1
i = 2 total = 3
i = 3 total = 6
i = 4 total = 10
i = 5 total = 15
i = 6 total = 21
i = 7 total = 28
i = 8 total = 36
i = 9 total = 45
i = 10 total = 55
total = 55 //forを抜け出したところ。
1から任意の数までの和を求めるためには、2−3のプログラムでは、その都度、プログラムを変え、コンパイルもやり直さないといけない。それでは不便なので、任意の数をコマンドライン引数で与えることにする。コンピュータの最大の特徴は、何万回、何億回もの繰り返し処理をあきもせず、高速で実行するところにあり、それが命。
1からコマンドライン引数で与えられる値までの和を求める
class Repeat2 {
public static void main(String[] args) {
int total = 0;
//1から引数args[0]で与えられた値までの和
System.out.println("args[0] = " + args[0]);
//String型の引数から、Integerクラスのstaticメソッドで整数化する。
int n = Integer.parseInt(args[0]);
for(int i = 0; i <= n; i++) {
total = total + i;
}
System.out.println("1から" + n + "までの和 = " + total);
}
}
[実行結果]
C:\JavaTry\Application1>java Repeat2 100
args[0] = 100 1から100までの和 = 5050
args[0] = 10000 1から10000までの和 = 50005000
args[0] = 100000 1から100000までの和 = 705082704
この結果はおかしい。int型(32ビット)の最大値 2147483647 内で納まらなかったのであろう。
int total = 0; を long total = 0; に変更すると問題なし(longは64ビット)。
args[0] = 100000 1から100000までの和 = 5000050000
args[0] = 1000000 1から1000000までの和= 500000500000
[コメント]
・コマンドライン引数は、文字型配列と同じ形をとる。引数が1ケなら、args[0]。
・計算処理をする場合には、数値にする必要があり、IntegerクラスのparseInt(s)メソッドを利用する。
(すでに2-4で解説ずみのことではあるが、再掲。)
・3-2,3-3の例題についての詳しい解説は、「JavaScriptの基本」2章 参照。
[参考]
int,long型に関連して、基本データ、8つの基本形をまとめたプログラムを紹介する。
サンマイクロ社の「Javaチュートリアル 第3版」に紹介があり、ネット上に、Variables にあるものである。
実行結果のみ、コメントをつけて紹介する。
The largest byte value is 127 // (8ビット符号つき整数)
The largest short value is 32767 //(16ビット符号つき整数)
The largest integer value is 2147483647 //(32ビット符号つき整数)
The largest long value is 9223372036854775807 //(64ビット符号つき整数)
The largest float value is 3.40282e+38 //(32ビット符号つき浮動小数点)
The largest double value is 1.79769e+308 //(64ビット符号つき浮動小数点)
The character S is upper case. //(16ビットUnicode文字データ)
The value of aBoolean is true
基本データは上記8通り ある。整数(宣言はint)、倍精度浮動小数点(double)、文字データ(char)が特に重要。
これらには、いずれもラップクラスといわれるクラスがある。
Integer,Double,Characterクラス などであり、
上記のプログラムでは、Integer.MAX_VALUE などのクラス変数、
Character.isUpperCase(aChar) のようなクラスメソッドを利用している。
多くのデータがある場合、個々に変数名をつけていては、それだけでも大変。そこで登場するのが配列である。一つだけ配列名を決めて、あとは0から始まる添え字で個々のデータを区別するものである。こうしておくと、添え字を繰り返し、forのカウンタ変数、iなどで処理するにも都合がよい。
[問題] 配列で与えられたデータの合計とその中の最大値を求める問題。
class ArrayDemo {
public static void main(String[] args) {
int total = 0;
int[] array = {300,165,12,50,63,36,200,125,111,100};
int max = 0;
for(int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
total = total + array[i];
if (array[i] > max) {
max=array[i];
}
}
System.out.println("");
System.out.println("total = " + total);
System.out.println("max = " + max);
}
}
[実行結果]
300 165 12 50 63 36 200 125 111 100
total = 1162
max = 300
[コメント]
1 この例では、配列名はarrayで、array[0]=300,array[1]=165,....,array[9]=100として、データを識別している。
2 配列の場合も型名は必要、整数型の配列ということうはっきりさせるため、int[] で初期化していることに注意。
3 合計を求める方は3度目だから、さほど問題はないだろう。
最大値の方は、変数 maxを最初はかりに0としておいて、forループの中のif文で配列の各要素とmaxとを比較
して、大きい方をmaxにしていくだけ。
4 少し配列データを変え、途中経過 を打ち出した。
class ArrayDemo {
public static void main(String[] args) {
int total = 0;
int[] array = {300,165,50,63,400,200,125,500,100};
int max = 0;
for(int i = 0; i < array.length; i++) {
total = total + array[i];
if (array[i] > max) {
max=array[i];
}
//途中経過
System.out.println("i = "+ i + " array[" + i + "] = " + array[i] +
" total = " + total + " max = " + max);
}
System.out.println("total = " + total);
System.out.println("max = " + max);
}
}
[実行結果]
i = 0 array[0] = 300 total = 300 max = 300
i = 1 array[1] = 165 total = 465 max = 300
i = 2 array[2] = 50 total = 515 max = 300
i = 3 array[3] = 63 total = 578 max = 300
i = 4 array[4] = 400 total = 978 max = 400
i = 5 array[5] = 200 total = 1178 max = 400
i = 6 array[6] = 125 total = 1303 max = 400
i = 7 array[7] = 500 total = 1803 max = 500
i = 8 array[8] = 100 total = 1903 max = 500
total = 1903
max = 500
[問題]乱数を6000回発生させて、サイコロの目に対応させて、それぞれの目の出現回数を見る。
(サイコロの目の数にみあう大きさの配列をとる)。
[解1]
class Saikoro {
public static void main(String[] args) {
int[] array= new int[6];
int ransu;
for(int i = 0; i < 6000; i++) {
ransu = (int)(6000.0 * Math.random());
if (ransu < 1000) {
array[0]++;
} else if (ransu >= 1000 & ransu < 2000) {
array[1]++;
} else if (ransu >= 2000 & ransu < 3000) {
array[2]++;
} else if (ransu >= 3000 & ransu < 4000) {
array[3]++;
} else if (ransu >= 4000 & ransu < 5000) {
array[4]++;
} else if (ransu >= 5000 & ransu < 6000) {
array[5]++;
}
}
for(int i = 0; i < 6; i++) {
System.out.println((i+1) + "の目 = " + array[i]);
}
}
}
[実行結果]
1の目 = 1009 2の目 = 994 3の目 = 1023
4の目 = 991 5の目 = 954 6の目 = 1029 合計 6000
[コメント]
1 2-5の例題では、配列の初期値がわかっていたので、直接データを与えるという簡略化した記述であったが、
今回は6ケという大きさしか分からない。
そこで、new演算子で配列に必要なメモリ領域 をとる(2-5は例外的にnew不必要、文字列の場合も同じ)。
もっとも、この場合も、int[] array={0,0,0,0,0,0};とすることもできる。
2 Math.random()は0〜1までのdouble型の乱数を発生する。これを6000倍して、(int)という型変換(キャスト)で、
0から6000までの整数にする。
1000まではサイコロの1の目としてarray[0]に1を足す、1000から2000までは2の目としてarray[1]に1を
足す ....と考えて、if (a){a1} else if(b){b1} else if(,,){...} と並べている。
(もしaが真ならa1を実行、そうでなく bなら b1を実行、...と続く)。
3 ransu = Math.round(6000.0 * Math.random()); のように、Mathクラスのメソッドroundを使って、
整数化することもできる。
(Math.random()とともに、API参照、2-2で出てきたクラス変数のMath.PIも見ておくこと)。
[解2]
class Saikoro2 {
public static void main(String[] args) {
int[] array= new int[6];
int ransu;
for(int i = 0; i < 6000; i++) {
ransu = (int)Math.round(6000.0 * Math.random());
for(int k = 0; k < 6; k++) {
if ((ransu / 1000) == k)
array[k]++;
}
}
for(int i = 0; i < 6; i++) {
System.out.println((i + 1) + "の目 = " + array[i]);
}
}
}
[実行結果]
1の目 = 976 2の目 = 993 3の目 = 1016
4の目 = 1007 5の目 = 1042 6の目 = 966 合計 6000
[コメント]
1 乱数で発生し、整数化した数は0から5999まで。1000で割った商は0から5までなので、その値が
インデックスとなる配列 に1足せばよい。
2 [解1]よりずっと簡略化されるが、考え方はやや難しいかもしれないが、プログラミングの主流、
アルゴリズムといわれるものは解2である。
3 乱数は発生させるたびに値が異なるので、目の出方はいつも同じではない。たまに、合計が、
解1、解2とも5999にしかならないことがある。理由は目下不明。
素数とは、1と自分自身以外では割り切れない数。2,3,5,7,11....などは素数であるが、1は素数にはいれない。
100までなら、25個(10までなら4個、1000までなら168個、1000までなら1229個)。
[プログラム]
class Sosu {
public static void main(String[] args) {
int number=100;
int[] sosu= new int[number/2];
sosu[0]=2;
sosu[1]=3;
int n=2; //nは素数の配列 sosu の添え字
for(int i = 5; i < number; i=i+2) { //偶数は最初から排除
boolean prime=true;
for(int j = 1; j < n; j++) { //jは、sosu の添え字、sosu[1]=3からチェック
if ((i % sosu[j]) == 0) {
prime=false; //iがいずれかの素数で割り切れれば、素数ではない。
break;
}
}
if (prime) {
sosu[n] = i;
n++; //次の素数を入れるために、sosu の添え字を1増やす。
}
}
System.out.println(number + " までの素数");
for(int j = 0; j < n; j++) {
System.out.print(sosu[j] + " ");
}
System.out.println(" ");
}
}
[参考]
「独習Java」の練習問題 4.5 の解答(CD-ROM)には、もっと短いプログラムがある。
ある数が素数であるかどうかを決めるのに、それまでのすべての数で割って、割りきれなければ、
素数として打ち出すということで、配列は使っていない。
無駄な処理も入るのと配列の事例として私の例を出す。複雑にはなる。
[実行結果]
100 までの素数
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
[コメント]
1 素数と決まった数をいれる配列をint[] sosu= new int[number/2]で用意する。numberはここでは100。
少なくとも、偶数は素数ではないので、配列要素はnumber/2としている。
2 3や5の倍数も除かれるので、それでも実際よりはずっと多いが、配列要素はあらかじめ決めておく必要がある。
(自在にできるものとして、VectorとかArrayListがある)。
3 if文の中の % は余りを求めるもの、== に注意。= を代入の意味で使うので、ほんとうに等しいことを
問うのは、==。
(すでに3-1で解説ずみのことではあるが、再掲。)
4 breakは、繰り返しforを打ち切るもの。それまでにわかっている素数の配列のすべてで余りを求めて
そのすべてで0でないなら(割り切れない)、その数も素数。
5 Javaの8つの基本データ型のひとつが真偽を示す booleanで、値はtrueとfalseしかない。
boolean primeがtrueなら素数であるとして、配列に加えていく。配列の添え字nも増やすという考え方である。
6 なお、繰り返しの制御変数 i は、素数候補の数でもある。偶数を除いていくため、奇数5から、
2ずつ増やしている。
そのため、素数配列の添え字のための制御変数 jも、sosu[1]=2 は不要となり、sosu[2]=3 から
チェックを始めている。
7 途中経緯を打ち出してみた。
if (prime) のブロックで、新たに素数がきまって、n++する直前に、下記をいれる。
System.out.println("i=" + i + " j=" + j + " n=" + n + " sosu[n]=" + sosu[n]); 結果は、
i=5 j=2 n=2 sosu[n]=5
i=7 j=3 n=3 sosu[n]=7
i=11 j=4 n=4 sosu[n]=11
i=13 j=5 n=5 sosu[n]=13
(途中省略)
i=79 j=21 n=21 sosu[n]=79
i=83 j=22 n=22 sosu[n]=83
i=89 j=23 n=23 sosu[n]=89
i=97 j=24 n=24 sosu[n]=97
サンマイクロ社の「Javaチュートリアル 第3版」に紹介があり、ネット上に、SortDemo にある例である。
二重の繰り返し(for)、繰り返し変数、iとiを配列の添え字として、極めて巧妙に使った例である。
[プログラム]
public class SortDemo {
public static void main(String[] args) {
int[] arrayOfInts = { 32, 87, 3, 589, 12, 1076,
2000, 8, 622, 127 };
for (int i = arrayOfInts.length; --i >= 0; ) {
for (int j = 0; j < i; j++) {
if (arrayOfInts[j] > arrayOfInts[j+1]) {
int temp = arrayOfInts[j];
arrayOfInts[j] = arrayOfInts[j+1];
arrayOfInts[j+1] = temp;
}
}
}
for (int i = 0; i < arrayOfInts.length; i++) {
System.out.print(arrayOfInts[i] + " ");
}
System.out.println();
}
}
[実行結果] 3 8 12 32 87 127 589 622 1076 2000
[コメント]
1 配列データを小さい順に並べ替えるのだが、配列データの隣同士の二つの数値を比較して、大きい方を
添え字 j の大きい方にもっていくことで、最大値を配列の最後尾にもっていく。
2 この場合の添え字 jの最終値はiそのものだが、最初、iは配列の大きさそのものである。
3 次は、それをのぞいた配列(添え字iを--して)にして、同じことを繰り返す。
二重の繰り返し(for)を極めて巧妙に使った例である。
4 内部の繰り返し(添え字 j) が終わったところに、途中結果打ち出しのprintln文をいれて、
以上を確かめてみる。
5 注意すべきは、原型のままでは、jについて「シンボルを見つけられない」とのエラーとなることである。
jのようなブロック内で宣言されたローカル変数、jはそのブロック内でしか有効 でないから。
6 下記に、修正、追加点と実行結果のみ記述。
int j; //追加
for (int i = arrayOfInts.length; --i >= 0; ) { //同じ
for (j = 0; j < i; j++) { //jのint宣言をとる。
........
}
}
System.out.println("i=" + i + " j=" + j + " arrayOfInts[j] =" + arrayOfInts[j] ); //追加
}
以下、同じ
[実行結果]
i=9 j=9 arrayOfInts[j] =2000
i=8 j=8 arrayOfInts[j] =1076
i=7 j=7 arrayOfInts[j] =622
i=6 j=6 arrayOfInts[j] =589
i=5 j=5 arrayOfInts[j] =127
i=4 j=4 arrayOfInts[j] =87
i=3 j=3 arrayOfInts[j] =32
i=2 j=2 arrayOfInts[j] =12
i=1 j=1 arrayOfInts[j] =8
i=0 j=0 arrayOfInts[j] =3
3 8 12 32 87 127 589 622 1076 2000
前節のコメント、4,5,6に途中結果をだしたが、それは、そのときの配列の右側、最大値のみを示したもの。
配列全体にわたる変化を含めて、もっと詳しい途中結果を出してみる。
なお、クラス名もSortDemo2と変更、arrayOfIntsという配列名は煩雑なので、簡単にarrayと変更した。
追加した部分は右側にコメントした。
[プログラム]
public class SortDemo2 {
public static void main(String[] args) {
int[] array = { 32, 87, 3, 589, 12, 1076,2000, 8, 622, 127 };
int j; //変更
System.out.print("元の配列 array[i] = " ); //追加
for (int i=0; i < array.length; i++) //追加
System.out.print(array[i] + " "); //追加
System.out.println(""); //改行のため追加
System.out.println("途中経緯"); //追加
for (int i = array.length; --i >= 0;) {
System.out.print("i=" + i + " array[j] = "); //追加
for (j = 0; j < i; j++) {
if (array[j] > array[j+1]) {
int temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
System.out.print(array[j] + " " ); //追加
}
System.out.println( " array[" + j + "] = " + array[j] ); //追加
}
System.out.println("ソート結果"); //追加
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " "); //ソート結果
}
System.out.println();
}
}
[実行結果]
元の配列 array[i] = 32 87 3 589 12 1076 2000 8 622 127
途中経緯
i=9 array[j] = 32 3 87 12 589 1076 8 622 127 array[9] = 2000
i=8 array[j] = 3 32 12 87 589 8 622 127 array[8] = 1076
i=7 array[j] = 3 12 32 87 8 589 127 array[7] = 622
i=6 array[j] = 3 12 32 8 87 127 array[6] = 589
i=5 array[j] = 3 12 8 32 87 array[5] = 127
i=4 array[j] = 3 8 12 32 array[4] = 87
i=3 array[j] = 3 8 12 array[3] = 32
i=2 array[j] = 3 8 array[2] = 12
i=1 array[j] = 3 array[1] = 8
i=0 array[j] = array[0] = 3
ソート結果
3 8 12 32 87 127 589 622 1076 20003 8 12 32 87 127 589 622 1076 2000
[コメント]
1 恥ずかしながら、これをやる過程でエラーを出して、はじめて下記の行の解釈を誤っていたことに気付いた。
for (int i = array.length; --i >= 0;) {
2 これは、--iとデクリメント記号が前置されているので、直ちに演算が行われる。
例では、array.lengthが10なので、繰り返しは i=9 から i=0 までとなる。
3 今まで通りのとデクリメント後置法なら、
for (int i = array.length-1; i >= 0; i--) { とするところ。
4 すると、for (j = 0; j < i; j++) { の行は、i=9 のとき、どうなるか?
j の繰り返しが 0から8 までで、最後の1項、array[9] が欠けるのではないかと考えさせられた。
5 if (array[j] > array[j+1]) { ということをしている。
たとえば、j=8 のとき、array[9] との比較をきちんとしていることになる。難解なプログラムである。
4-1、配列の最大値を求め、それを配列の右側に移動、最大値を示した配列の添え字も記憶しておき、
置き換えられた配列の右側と置き換えてみる。考え方は分かりやすいかもしれない。
[プログラム]
public class SortDemo2 {
public static void main(String[] args) {
int[] array = { 32, 87, 3, 589, 12, 1076,
2000, 8, 622, 127 };
int j;
for (int i = array.length; --i >= 0; ) {
int max=0; //配列の最大値
int n=0; //最大値のときの配列添え字
for (j = 0; j <= i; j++) {
if (array[j] > max) {
max=array[j];
n=j;
}
System.out.print(array[j] + " " ); //途中経過、もとの配列
}
j=j-1; //forの中でjが配列要素数より大きくなったため
int temp = array[j];
array[j]=max; //最大値を配列の右側へ
array[n]=temp; //配列の右側にあった値を最大値のあった位置へ
System.out.print( " max = " + max + " n = " + n); //途中経過
System.out.println( " array[" + j + "] = " + array[j] ); //途中経過
}
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " "); //最終結果
}
System.out.println();
}
}
[実行結果]
32 87 3 589 12 1076 2000 8 622 127 max = 2000 n = 6 array[9] = 2000
32 87 3 589 12 1076 127 8 622 max = 1076 n = 5 array[8] = 1076
32 87 3 589 12 622 127 8 max = 622 n = 5 array[7] = 622
32 87 3 589 12 8 127 max = 589 n = 3 array[6] = 589
32 87 3 127 12 8 max = 127 n = 3 array[5] = 127
32 87 3 8 12 max = 87 n = 1 array[4] = 87
32 12 3 8 max = 32 n = 0 array[3] = 32
8 12 3 max = 12 n = 1 array[2] = 12
8 3 max = 8 n = 0 array[1] = 8
3 max = 3 n = 0 array[0] = 3
3 8 12 32 87 127 589 622 1076 2000
独習Java」4章末尾の「理解度チェック」(P124)にあったもの、4節を補強するため、途中経緯を付加して掲載する。
[問題]
複数の引数を受け取り、それを数値に変換して、昇順に表示する。
[プログラム]
class SortNumbers {
public static void main(String args[]) {
// 配列の宣言,割り当て,初期化
int size = args.length;
double data[] = new double[size];
int j;
for(int i = 0; i < size; i++)
data[i] = Double.valueOf(args[i]).doubleValue();
for(int i = 0; i < size - 1; i++) {
System.out.print("i=" + i + " data[j]="); //追加
for(j = i; j < size; j++) {
if(data[j] > data[i]) {
double temp = data[j];
data[j] = data[i];
data[i] = temp;
}
System.out.print(data[i] + " " ); //追加
}
System.out.println( " data[" + i + "] = " + data[i] ); //追加
}
System.out.println("配列を昇順に表示するソート結果"); //追加
for(int i = 0; i < size; i++)
System.out.print(data[i] + " ");
System.out.println("");
}
}
[実行結果] (途中経緯も含む)
D:\JavaTry\Application1>java SortNumbers 25.0 5.0 100.0 50.0 75.0
i=0 data[j]=25.0 5.0 25.0 50.0 75.0 data[0] = 100.0
i=1 data[j]=5.0 5.0 25.0 50.0 data[1] = 75.0
i=2 data[j]=5.0 5.0 25.0 data[2] = 50.0
i=3 data[j]=5.0 5.0 data[3] = 25.0
配列を昇順に表示するソート結果
100.0 75.0 50.0 25.0 5.0
[コメント]
1 for(int i = 0; i < size - 1; i++) {
for(int j = i; j < size; j++) {
i は i < size ではなく、ひとつ少ない繰り返し。ここではsize=5だから、i=0,1,2,3 だけ。
2 i=0 のときは、j は 0から < sizeまで繰り返し、最大のデータを左端に移動。
3 i=1 のときは、jは1からの繰り返しで、最大のものを除外して処理し、2番目に大きいものを左端に。
4 i=2 のときも同様。
5 i=3 の処理後は、data[4] には、最小のデータのみが残ることになる。
上記の途中経緯に乱れがあるのが気になる。そこで、4-4の流儀で、配列の後ろから、一つ前と比較
して、大きい方を前へもっていくように変更した。
下記のjは、常に配列の右端から始め、iより大きい間、繰り返す。iは、0,1,2,3,4と増えていくので、次々と
左端(大きい数が入っている)を無視して処理していく。
[プログラム]
class SortNumbers2 {
public static void main(String args[]) {
int size = args.length;
double data[] = new double[size];
int j;
for(int i = 0; i < size; i++)
data[i] = Double.valueOf(args[i]).doubleValue();
for(int i = 0; i < size; i++) { //変更
System.out.print("i=" + i + " data[j]="); //追加
for(j = size-1; j > i; j--) { //変更
if(data[j] > data[j-1]) { //変更
double temp = data[j-1]; //変更
data[j-1] = data[j]; //変更
data[j] = temp; //変更
}
System.out.print(data[j] + " " ); //追加
}
System.out.println( " data[" + i + "] = " + data[i] ); //追加
}
System.out.println("配列を昇順に表示するソート結果"); //追加
for(int i = 0; i < size; i++)
System.out.print(data[i] + " ");
System.out.println("");
}
}
[実行結果] (途中経緯も含む)
D:\JavaTry\Application1>java SortNumbers2 25.0 5.0 100.0 50.0 75.0
i=0 data[j]=50.0 75.0 5.0 25.0 data[0] = 100.0
i=1 data[j]=50.0 5.0 25.0 data[1] = 75.0
i=2 data[j]=5.0 25.0 data[2] = 50.0
i=3 data[j]=5.0 data[3] = 25.0
i=4 data[j]= data[4] = 5.0
配列を昇順に表示するソート結果
100.0 75.0 50.0 25.0 5.0
以上で、Java入門1 本文は終了、5章以降はJava入門2(Application応用) へ
2-2 繰り返しの例題を使い、cdによるディレクトリ(フォルダ)変更やjavacによるコンパイル時の、
タイプミス、不注意による典型的なJavaのエラーメッセージの一例を示す。
1 //cd z:\Java Try\Application1
2 //javac Repeat.java
3 //set CLASSPATH=.
4 //java Repeat
5
6 class Repeat {
7 public static void main(String[] args) {
8 int total = 0;
9 //1から10までの和
10 for(int i = 0; i <= 100; i++) {
11 total = total + i;
12 }
13 System.out.println("total = " + total);
14 }
15 }
1 Z:>cd z:\Java Try\Application1 指定されたパスが見つかりません。 JavaTryと空白なしで作ったはず。ディレクトリ(フォルダ)名を間違えるとこうなる。 2 Z:>//cd z:\JavaTry\Application1 ファイル名、ディレクトリ名、またはボリューム ラベルの構文が間違っています。 //は、Javaに対するコメントで、これをコマンドプロンプトで使うときは//を使ってはいけない。 3 Z:>z:\JavaTry\Application1 'z:\Java' は、内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチ ファイルとして 認識されていません。 cd z:\JavaTry\Application1としなくてはいけない。cdを忘れている。
4 Z:\JavaTry\Application1>javac Repet.java エラー: Repet.java を読み込めません。 javacの対象ファイル名のスペルミス。cdを実行していないときも、こうなる。 5 Z:\JavaTry\Application1>javac Repeat.jave javac: Repeat.jave は無効なフラグです。 使い方: javacjavacの対象ファイルの拡張子は、java 以下、使用可能なオプションとして、ずらずら出てくる。 6 z:\JavaTry\Application1>javac Repeat.java Repeat.java:12: ';' がありません。 } 11行目の区切り記号;を忘れると、次の行で注意が来る。 7 Repeat.java:7: ')' がありません。 public static void main(String[] args { ^ 丸括弧を閉じるのを忘れた場合、小さな山印に注意。 その近辺にエラーがある。 8 Repeat.java:7: シンボルを見つけられません。 シンボル: クラス Spring 場所 : Repeat の クラス public static void main(Spring[] args) { ^ 「シンボルを見つけられない」とは、StringのようにJavaできまっている用語を間違えた場合に出る他、 次の9,10などのケースもある。 9 int とか doubleとか、型を定義した変数のスペルミス tota1 = total + i; ^ totalのエルを1(イチ)と間違えた。 10 変数に型定義をしなかった場合 for(i = 0; i <= 100; i++) { ^ i ははじめて使う変数なのに、intと型定義を忘れたため。 (以下、iを使っているところ、すべてにこのエラーが出る他 Repeat.java:11: 演算子 + は int,i に適用できません。 total = total + i; とか、 Repeat.java:11: 互換性のない型 検出値 : 期待値 : int total = total + i; などのような恐ろしげなそっセージまで、6個もエラーが出る) 11 メソッド、クラスを閉じる中カッコを忘れた場合 Repeat.java:15: '}' がありません。 12 ダブルクォーテーションを忘れた場合 Repeat.java:13: 文字列リテラルが閉じられていません。 System.out.println("total = + total); ^ Repeat.java:14: ')' がありません。 と、エラーは2個になる。 13 10行目、<= を離した場合 Repeat.java:10: 式の開始が不正です。 for(int i = 0; i < = 100; i++) { ^ 14 空白に全角を使った場合 Repeat.java:13: \12288 は不正な文字です。 System.out.println("total = " + total);
1 タイプミスなどを修正した後 再度、javacからやり直す必要がある。 一度、打ったコマンドは上向きの方向キーでコピーされる。 2 実行もした後、プログラムの手直しをした場合も、javacからやり直す必要がある。 上向きキーを繰り返し使ったり、場合によっては、下向きのキーで、さかのぼって、以前に打った コマンドが使える。
コマンドプロンプトにて、右クリックー範囲の指定 として、下向き及び右向きの
方向キーで、範囲を指定する。白くなった部分で、右クリック してxyzzyにはりつける。
その部分は、Javaのプログラムとは関係ないから、コメントにする。
先頭に/*、最後に*/ とすれば、その間がコメントになる。
文章やプログラムの複写には下記キー使用 が便利。
選択 shift+方向キー(長文のときは、先頭でクリック、最後でshift+クリック)
コピー(クリップボードへの) ctrl+Insertキー
貼り付け shift+Insertキー
なお、移動は、一般には、shift+Deleteキーだが、xyzzyでは、ctrl+Deleteキー。
[注意] ctrl+Insertキーとするとき、ctrlキー以外だったら、選択していた青い色が消えない。
こういうときは、Insertキーのみ有効となり、xyzzyで打ったものがが上書きされて、
前のが消えていく。再度、Insertキーを押して上書きを解除すること。
コマンドプロンプトへの貼り付けは、shift+Insertキーが使えないので、右クリックー貼り付けと
せざるをえない。
たとえば、2-2の例題は、2-1の例題をもとに作成するのがよい。プログラムは似たようなコマンドの羅列だから。 2-1の例題をxyzzyで読み込み、名前をつけて保存として、2-2からクラス名と同じ Repeat.javaとする。 タイトルバー でファイル名が変わったことを確認してから、必要な修正、追加をして、 上書き保存、コンパイル、実行。 (htmlファイルで使った方法に同じ)。
![]() |
![]() |
![]() |