最終更新日 |
文科系の学生に、前期、ホームページ作成をしてもらった後、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 は無効なフラグです。 使い方: javac
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ファイルで使った方法に同じ)。