Java プログラミング入門 福永 力 2003 年 4 月初版/04 年 4 月、05 年 6 月改訂 目次 1 最初の一歩 1.1 1.2 1.3 2 3 プログラムの作成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1 ソースファイルの作成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2 1.1.3 1.1.4 プログラムのコンパイル、バイトコードの作成と実行 . . . . . . . . . . . . . . . コメントを書く . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 課題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . プログラミングを学ぶにあたって . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HelloWorld プログラムの分析 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 3 4 5 5 7 1.3.1 1.3.2 1.3.3 コメント . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 8 8 1.3.4 1.3.5 system クラスのメソッド読み出し . . . . . . . . . . . . . . . . . . . . . . . . . . String[] args を使う . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 9 Java 言語の概要 2.1 変数と演算子 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 Java プログラムの基本要素 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 15 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.2 2.3 2.4 2.5 クラスの定義 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . メソッド main の定義 変数と変数名 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 演算子 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 変数、演算子などのプログラム例題 . . . . . . . . . . . . . . . . . . . . . . . . . 課題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 式、文およびブロック . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.1 2.2.2 式(Expressions) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.3 2.2.4 ブロック(Blocks) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 文(Statements) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 課題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . コントロール文 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 19 21 21 23 24 24 2.3.1 2.3.2 課題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 25 26 2.3.3 2.3.4 for 文による繰り返し、特定回数の繰り返し . . . . . . . . . . . . . . . . . . . . 課題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 27 配列 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.1 課題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . if 文による条件判断分岐 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 30 31 課題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.5.1 while、do-while 文、不特定回数の繰り返し 1 . . . . . . . . . . . . . . . . . . . . 2.6 3 2.5.2 配列を使ったプログラミング課題 . . . . . . . . . . . . . . . . . . . . . . . . . . 参考 Web ページ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 数値計算と class Math の利用 3.1 class Math の変数、メソッド . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 36 3.2 3.3 Math を使ったさまざまな例 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 反復的に方程式 f (x) = 0 の解を求める . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 37 はじめに . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 37 39 3.3.1 3.3.2 3.3.3 3.4 3.5 3.6 二分法(はさみうち法) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Newton-Raphson 法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 数値積分とメソッドの自作 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 乱数発生とシミュレーション . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.5.1 3.5.2 3.5.3 4 32 35 ダイスゲーム . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ゲームの理論:囚人のジレンマ . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 43 45 課題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 石ころなげゲーム:的に当たる数は? . . . . . . . . . . . . . . . . . . . . . . . . . アプレット(Applet)を使って図形を描く 4.1 4.2 4.3 4.4 40 42 46 画面構成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Class Graphics を使った基本作図 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.1 4.2.2 4.2.3 基本図形要素描出 applet プログラム例 . . . . . . . . . . . . . . . . . . . . . . . 4.2.4 4.2.5 色指定 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . DisplayShape.java の分析 I Applet クラスの継承、Graphics クラスの具体化 . . DisplayShape.java の分析 II Graphics クラスの基本作画メソッド . . . . . . . . 46 46 46 49 50 文字列 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 52 4.2.6 Font について . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 参考 Web ページ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 課題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 54 55 2 最初の一歩 1 プログラムの作成 1.1 ソースファイルの作成 1.1.1 これから Java 言語を学び、Java でプログラムを書き出していくのだが、そのためにはプログラム記 述用のエディタを用意しなければならない。Linux では vi、emacs、あるいは redhat CDE にあるシステ ム固有のもの、Windows ではメモ帳、秀丸、TeraPad などである。Word を利用することも原理的に可 能だがフォントを変えたり行端をそろえたりするわけでないのでシンプルなもので十分である。Linux では emacs、Windows では TeraPad というところか、標準的には。 現在の OS(Operating Soft:Windows とか Linux という基本ソフト)のもとで、そのいずれかを起動。 エディターで以下の文章(短いながら java プログラムです)を入力する。このオリジナルなファイルを ソース(源、原始)ファイルという。 class HelloWorld { public static void main(String args[]) { System.out.println("Hello, World!") ; } } プログラムを保存、ファイル名は HelloWorld.java とする。いまのところ各行のもつ意味合いについ てはおいておくことにする。 プログラムのコンパイル、バイトコードの作成と実行 1.1.2 Windows ではコマンドプロンプト、Linux では端末ウィンドウを空けて以下のようなコマンドを打鍵 する。 [prompt~ ]# javac HelloWorld.java [prompt~ ]# java HelloWorld Hello、World! [prompt~ ]# 気をつけるべきポイントは • ファイル名の前半部(.java より前)は一字一句大文字小文字も考慮してプログラムの先頭行 class ... の... と同じでなければならない。今の場合 HelloWorld。 • javac(翻訳指示)ではファイルのフルネームを入れる。 • java(実行指示)ではファイル名の前半部のみを指定。厳密にいうと実行すべきクラス名を指定す る。しかし第 1 項の制約により今のところファイル名前半部=(プログラム先頭で示したこのプ ログラムの)クラス名であるのでここではファイル名の前半部と考えておいて問題ない。 上記1行目(javac)で Java の翻訳結果であるバイトコードを作成する。この javac は HelloWorld.java を入力し、HelloWorld.class というファイルを出力する。この作業をコンパイル(compile)と呼んでい る。この中にバイトコードが含まれている。このバイトコードはコンピュータの OS によらない、独立 したコードになっている。これは Java 仮想マシーンの機械語で構成される。 3 つまりこの HelloWorld.class があれば、このファイルを Linux にもっていって実行させても Windows で実行させても変わりなく同じ実行結果が得られる。再コンパイルさせる必要がない。そのかわり各 OS はこのバイトコードを解釈して実行させる仮想マシーン(プログラムのこと)をもっていなければなら ない。このマシーンが搭載されていれば OS の差異は意識せずプログラムを記述できる。この仮想マシー ンを java と呼ぶ(上記 2 行目参照)。 言い換えると java というアプリケーションプログラムは仮想マシーンと呼ばれるものでありそれは バイトコードを解釈して今の自分の OS のもとでそのコードをあたかも自分の OS の独自のアプリケー ションとして実行させることにある。 1.1.3 コメントを書く 上のプログラムは以下のように書いても実行においてなんら影響はない。/*......*/とか//をコメント (ライン)と呼ぶ。これはコンピュータのためというよりプログラムを書いた人、読む人の「覚え」で ある。 /** * The HelloWorld クラスは"Hello World!"という * メッセージを標準出力に表示します。 **/ public class HelloWorld { public static void main(String[] args) { // "Hello World!"と表示 System.out.println("Hello World!"); } } 4 1.1.4 課題 • Hello, 都立大学! とプリントさせるようにして、再度 javac、java を行って実行させる。 • Hello, TMU! I am Sen-ichi Hosiho, 0234567 のように 2 行プリントさせるようにプログラムを改造。実行させよ。2 行目の名前は各自の、数字 は各自の学修番号とする。 • 上記の 2 行のプリントを1つの System.out.println で行えるようにするにはどうしたらいいか考 えよ、そして試してみよ。 ヒント:改行、ベルをならす、タブストップなどのコントロールはエスケープシーケンスというエ スケープ+制御文字の組み合わせの文字列で実行される。\(半角で¥あるいはバックスラッシュ) でエスケープを意味しその後にアルファベットか数字 1 文字で制御を指定する。\n で改行を意味 する。\7 でベル(ビープ)、\t でタブストップなど。だから System.out.println("ABC\nDE\F") ; を実行させると出力は ABC DE F となる。 1.2 プログラミングを学ぶにあたって プログラムを通してなにができるかその基本を押さえておこう。その基本は 3 点ありどんなプログラ ムもつまるところこの 3 点が複雑にからみあって成り立っているものであるということ。ここでは当面 データ構造やプログラム書法については別のこととしておく。その 3 点とは 1. プログラムは順序立てて上から下に実行される、 2. ある順序立てを繰り返す、繰り返し、 それと 3. ある順序立てをある条件では実行し、その条件が満たされなかったら実行しない、もしくはちが う順序立てを実行する条件分岐 である。まだやっとひとつのプログラムを作り上げたばかりであるが、それらについてごく簡単に見て いこう。細かい意味合いについては不問にしていく。 まず以下の例である。エディターで作成したら GreetingSample.java として保存しよう。 class GreetingSample { public static void main(String args[]) { System.out.println("Good morning!") ; System.out.println("How are you?") ; System.out.println("Fine, thank you") ; System.out.println("Good bye!") ; } } 5 単純にプログラム頭部から 1 行、1 行実行がなされていく。 次の例をみてみよう。繰り返しの例である。LoopSample.java として保存しよう。 class LoopSample { public static void main(String args[]) { int loop, max=10 ; System.out.println("************") ; for(loop=0;loop<max;loop++) { System.out.println("* *") ; } System.out.println("************") ; } } 実行結果をみてみると以下のようになる。 ************ * * * * * * * * * * * * * * * * * * * * ************ このプログラムを実行してみると 10 回{ System.out.println("* *") ;}という{....}の中の 順序立て(この場合1命令のみ)を繰り返しているということが理解される。これが 2 番目の繰り返し の実例である。 そしてこのプログラムを改造して 3 番目のポイントである条件分岐を概観しよう。以下のプログラム は LoopIfSample.java とする。 class LoopIfSample { public static void main(String args[]) { int loop, max=12 ; for(loop=0;loop<max;loop++) { if(loop == 0) { System.out.println("************") ; } else if (loop == (max-1)) { System.out.println("************") ; } else { System.out.println("* *") ; } } } } 繰り返しを意味する for(....) {...}の{...}なかに if (...){...} else {...}という形の構造が 表れているのに注意。これはもし()内が成り立つのであればすぐ直後の{...}内の順序立てを、そう でないなら(else)else のあとの{...}の中の順序立てを行えという意味にとればよい。ほぼ英語の文 意である。else if はそうではないがもしととればよい。上の例では loop の値が 0 のとき、あるいは max-1(=11) のとき上蓋、下蓋を意味する************をそうでないなら側壁を意味する* * を印字せよといっていることになる。 ここまでで注意すべき点として • 最初の行の出だしは常に class となっている。そのあとに何かしらプログラムの名前を意味するよ うなものが出てきている。そしてそれはそのプログラムを保存する際のファイルネームの主幹を なす(あとは.java で閉じる)。 6 • 中括弧{と}がプログラムの構造を形作っている。常にかっこ開くとかっこ閉じる記号はバランス されていなければならない。プログラムの構造とはいまだ意味合いはわからないが逆に{...}をそ う思ってしまえばよいようだ。 • 各行開始欄が微妙に行によって異なっている。しかしそれは先ほどみたプログラムの構造と関連 している。 HelloWorld プログラムの分析 1.3 前節で一般にプログラム言語でなにができるかを概観した。順序立てた実行、繰り返し、それと条件 判断と分岐であった。そして Java で書かれた具体的なプログラムの例をいくつか見た。 その内容、プログラムの計算概要は違うが、そのすべてに共通する構造があるのに気がついただろう か。とくに最初の 2 行はほとんど同じであるといってよい。1 行目の class のあとの文字列が異なっては いるが。 はじめのうちはこの 2 行は Java でプログラムを書く場合にはつねにこのように指定するものだ、と 決めてかかってもよい。しかしプログラムの分析を通してこれらについてここで少し基礎的な素養を学 んでおこう。 クラス、オブジェクト、具体化(インスタンシエーション)、変数、メソッドなど聞きなれぬことばが でてくるが、言葉は覚えておいても関係や具体的な意味は漠然ととらえておいてよいだろう。 前回のプログラムを 1 行 1 行振り返りながら java の基本要素を概観しよう。ここで HelloWorld.java を再掲しておこう。 /** * The HelloWorld クラスは"Hello World!"という * メッセージを標準出力に表示します。 **/ class HelloWorld { public static void main(String[] args) { // "Hello World!"と表示 System.out.println("Hello World!"); } } 1.3.1 コメント /** * The HelloWorldApp クラスは"Hello World!"という * メッセージを標準出力に表示します。 **/ // "Hello World!"と表示 前節(1.1.3)でも記述したようにプログラムを読みやすくするために、コメントを多くプログラム内に 埋め込もう。コメント=直接プログラムの演算結果には影響をあたえない。人間(プログラマーとプロ グラムを読む人の備忘録) 1. /* .... */で数行(1 行以上)を指定。 2. //でそれ以降改行までをコメントとする。 7 1.3.2 クラスの定義 public class HelloWorld{...} クラスは java、C++のようなオブジェクト指向言語ではもっとも基本要素となる。クラスにそのクラス を運営するのに必要なデータ、さまざまな記述(関数)を定義する。記述(関数)をメソッド(Method) という。つまりクラスとはその中で使われる変数(後述)とそれを操作、加工するメソッドよりなる。 つまり java では class MyClass { ......} というのがあると ࢠ ࣚ ࢪ ࡡ ᏽ ⩇ MyClass { ን ᩐ ᏽ ⩇ ࣒ ࢮ ࢴ ࢺ 㸝 㛭 ᩐ 㸞 ᏽ ⩇ } の構造が含まれていることになる。class はキーワード。MyClass は任意の名前(変数名=固有名詞)で 識別子と呼ばれる。識別子は java の変数や定数、あるいはメソッドの名前のことである。識別子の名前 付け規則というものがある。固有名を文字(アルファベット)ではじめ文字(アルファベット)、数字で 構成される。アルファベットの大文字小文字は区別される。文字列の長さに制限はない。慣例としてク ラス名は大文字で始め、変数、メソッド名は小文字で始める。途中単語の最初は大文字で他は小文字に する。なお java の中で特別な言葉(複数)はキーワードとして利用が限定されている。これらを各種識 別子として利用することはできない。 いままでの HelloWorld には変数の定義はない(class の名前を除いて)。メソッドとして main が定義 されている。クラス定義が具体化(インスタンス)されたものをオブジェクトとよぶ。 長く複雑な java プログラムはその中に独自のクラスをいくつももち、また他の人々が書いたクラスも 数多く利用する。しかし当面はただひとつのクラスからなるプログラムを考察しよう。この場合このク ラスは必ず main メソッドを持たなければならない。 1.3.3 メソッド main の定義 public static void main(String[] args){...} あるひとつの簡単な java プログラムを作ったら実行はまず main メソッドの先頭行から実行されていく ことになる。 main には 3 個の修飾子(モディファイヤ)がある。それらは public static void となっている。当面 この修飾子を無視して進めても(つまりこの3つは常に main の前に決まってこの順序で並べるのだが) 簡単に説明しておく。 1. public をつけてこのクラスがどのオブジェクト(あるクラスが具体化される=インスタンス、ある いはオブジェクト化)からもわけ隔てなく呼び出せるようにする。 public でないものは private。 わけがあってそのメソッドを他のオブジェクトで利用されては困る場合 private でメソッドを定義。 2. static をつけてこのメソッドがクラスの普遍的な関数(メソッド)であることを指定。static をつ けないメソッドはクラスがインスタンス化されてオブジェクトとなるが、そのオブジェクトのみ がそのメソッドを利用できる。クラスがインスタンス化されてオブジェクトになった場合、この static で定義された変数、メソッドはクラスからオブジェクトへそのままコピーされる。したがっ ていくつもクラスがインスタンス化されてもっすべてから共有されるもの(変数、メソッド)と なる。 8 3. void をつけるとこのメソッド(関数)は戻り値がなにもないことになる。関数について以下の文 章を参照。この関数(メソッド)に戻り値がない場合 void として明示しておく。 main メソッドにはあらかじめ決められた引数がある。String[] args である。これはあとで課題として実 習しよう。 Java でのメソッド(関数)の定義は基本的に以下のような形式になっている。 戻り値型指定 メソッド名(引数) メソッドでなにか計算した結果を返えす場合、その結果を戻り値と呼ぶが、その型を指定する(型(type) とはあとで議論する)。引数とはメソッドに入力させる変数値である。アーギュメントとかパラメータ とかいったりする。引数は複数あってかまわない。ちょうど数学の関数で y = f (x1 , x2 , . . . , xn ) としたとき xi を関数fへの引数(パラメータ)とし、y を関数fの戻り値とみなすようなものである。 1.3.4 system クラスのメソッド読み出し System.out.println("Hello World!");} System.out でクラス System の変数を参照(System.out)することになり、それはもうひとつ他のクラ ス PrintStream クラスを具体化したものを意味するようになる(ことになっている=決まり事)。println は PrintStream のメソッドである。この場合 println は”で囲まれた文字列(リテラル)を標準出力装置 に印字(タイプ)するということを行う。だから System.out.println はオブジェクト System.out のメ ソッド println を呼び出していることになる。 1.3.5 String[] args を使う public static void main(String[] args) {...} main メソッドの引数 String[] args を使うことを考えてみよう。java のクラスを java コマンドを用いて 実行させるとき、パラメータを与えることができる。たとえばクラスの名前が以下のように MessageOut だとすると、 MessageOut 以下、スペースで区切られた各部分の文字列(全部で 4 部分)が args[0]="Go", args[1]="Go", args[2]="Hanshin", args[3]="Tigers!" と args という変数名のなかの第 0 成分から第3成分までに 格納されたことになる。以下のプログラムを作成し、実行させてみてその理解を深めてみよう。保存す るプログラムの名前は MessageOut.java(大文字、小文字もこのとおり)とする。 /** * 課題:実行時に与えられた文字列(ストリング)の表示 */ public class MessageOut { public static void main(String[] args) { System.out.println("Hello TMU!"); System.out.println("少しこった java プログラム") ; System.out.println("今プログラム起動時に入力したメッセージは: ") ; System.out.println(args[0]) ; System.out.println(args[1]) ; System.out.println(args[2]) ; System.out.println(args[3]) ; } } 9 上記プログラムを以下のようなステップで実行する。 [prompt~]# javac MessageOut.java [prompt~]# java MessageOut Viva Our Hanshin Tigers! Hello TMU 少しこった java プログラム 今プログラム起動時に入力したメッセージは: Viva Our Hanshin Tigers! 10 Java 言語の概要 2 変数と演算子 2.1 2.1.1 Java プログラムの基本要素 この節で学ぶことはプログラムの基本要素である。この基本要素は多かれ少なかれどのプログラ ム言語でも共通に備えている。したがってなんらかのコンピュータ言語でプログラムを書こうとするな らまずこの基本要素を押さえておかなければならない。 以下の java プログラムを BasicsDemo.java としよう。 class BasicsDemo { public static void main(String[] args) { int sum = 0; for (int current = 1; current <= 10; current++) { sum += current; } System.out.println("Sum = " + sum); } } 簡単なプログラムだけど、非常に多くの概念をこの例は含んでいる。最初の2行はこれは Java の特 徴で、この部分は各種プログラミング言語によって書き方が異なる。一般的にヘッダー(頭部)と呼ば れている。さらにその他の基本要素はこのプログラム内にほぼ全部入っている。それらは 変数 データを一時的に保持するもの、名前(看板)と型(データタイプ)がある。 演算子 算術、論理演算、代入に必要な演算単位、つまり足すとか引く, かけるとかわるとか。 式、文、ブロック 演算子と変数の組み合わせで作る式、プログラムの基本単位、さらにそれを含む文、 文のかたまり単位のブロック。 コントロール文 条件にしたがってプログラムの実行の流れを制御するための文、繰り返しや条件分岐。 2.1.2 変数と変数名 上記プログラム(BasicsDemo.java)では int sum と int current という表記がある。これが変数宣 言をしている。プログラム内で使う変数はすべて最初に型指定しなければならない。一般的には type name という形式をとる。type は型と日本語で訳される。 (基本データ)型には以下の表の Keyword のどれ かを指定する。詳しくは Java Tutorial HP http://java.sun.com/docs/books/tutorial/java/nutsandbolts/datatypes.html 11 を参照のこと。 Keyword Description Size/Format (integers) 整数 byte short int Byte-length integer Short integer Integer 8-bit 長 16-bit 長 32-bit 長 (一般的) long Long integer 64-bit 長 float Single-precision floating point 32-bit 長 (IEEE 754) double Double-precision floating point 64-bit 長 (IEEE 754) (real numbers) 実数 (other types) その他 char A single character 16-bit Unicode 文字 boolean A boolean value (true or false) true or false また以下の例のように int unInt = 4 などと書いて初期値を指定できる。 以下は MaxVariablesDemo.java というプログラム。 class MaxVariablesDemo { public static void main(String args[]) { // integers(整数) byte largestByte = short largestShort int largestInteger long largestLong = Byte.MAX_VALUE; = Short.MAX_VALUE; = Integer.MAX_VALUE; Long.MAX_VALUE; // real numbers(実数) float largestFloat = Float.MAX_VALUE; double largestDouble = Double.MAX_VALUE; // other primitive types(その他の基本型) char aChar = ’S’; boolean aBoolean = true; // display them all(それらを全部示すと) System.out.println("The largest byte value is " + largestByte); System.out.println("The largest short value is " + largestShort); System.out.println("The largest integer value is " + largestInteger); System.out.println("The largest long value is " + largestLong); System.out.println("The largest float value is " + largestFloat); System.out.println("The largest double value is " + largestDouble); if (Character.isUpperCase(aChar)) { System.out.println("The character " + aChar + " is upper case."); } else { System.out.println("The character " + aChar + " is lower case."); } System.out.println("The value of aBoolean is " + aBoolean); } } 変数名には以下の原則がある。 1. 無制限長の文字列で最初の文字はアルファベット(途中空白を含むことはできない)、特殊文字と して_と$。 • 許されるもの:ch_rad,GoodBye,theSister,onePoint,def$Int 12 • だめなもの:2to10,key.Point,J-wave • 同じようだが異なるものとして区別されるもの:I_am_Man と i_am_man 2. Keyword として Java が登録しているものは使えない。またこれ以外に true、false、null もだめ。 以下のものが使えない: Keywords abstract default if private throw boolean break do double implements import protected public throws transient byte case catch else extends final instanceof int interface return short static try void volatile char class finally float long native super switch white const continue for goto new package synchronized this 3. Java プログラムの慣習で変数は最初の文字を小文字で、クラス名は大文字で命名する。もし変 数名が2単語以上であれば2つめの単語からは最初の文字を大文字で、例:isVisible、myLove、 anyCountDown。 4. 変数名はスコープ(Scope)内で有効である。 スコープとはその変数名の有効な領域をいう。スコープが違えば同じ名前で違う型、意味をもたせる ことができる。その場合、そのように再宣言しなければならない。変数のスコープはどこで変数が定義 されているかによる。 定義されるところによってメンバー変数、局所(Local)変数、メソッドパラメータなどと分類され る。以下の図を参照。 基本データ型には次のように対応するラッパー(Wrapper)クラスがある。特定の基本データ型に対応 するクラスの変数、メソッドを使ってそのデータ型の基本処理を行うことができる。 13 型 ラッパークラス bool Boolean char byte Character Byte short int long Short Integer Long float double Float Double したがって上記プログラム中 int largestInteger = Integer.MAX_VALUE; double largestDouble = Double.MAX_VALUE; は Integer(Double)クラスの中に定義されているデータ MAX_VALUE の値を自分で定義した変数(型 を指定して)に代入している。その型で一番大きい値を示している。 MaxVariableDemo.java の実行例は以下のようである。この中で float、double などの実数の最大値 データ表示の末尾に E38 とか E308 となっているのは前半の小数点値× 1038 あるいは× 10308 を意味し ている。これを指数(Exponential)表示という。たとえば 1.284E2 というのはつまり 128.4 である。 The The The The The The The The largest byte value is 127 largest short value is 32767 largest integer value is 2147483647 largest long value is 9223372036854775807 largest float value is 3.4028235E38 largest double value is 1.7976931348623157E308 character S is upper case. value of aBoolean is true 以下は整数、実数それぞれを 2 つ以上の精度のキーワードで宣言させちょっとした計算をさせるプロ グラムである。ファイルは variableDemo.java class variableDemo { public static void main(String args[]) { int index,base,two ; float x,y,z ; double xd,yd,zd ; index=5 ; two = 2 ; base=index*two ; //* は乗算を意味する x=0.1f ; xd=0.1 ; y=x*base ; //整数と実数の乗算 yd=xd*base ; System.out.println("base is "+base) ; System.out.println("y is "+y) ; System.out.println("yd is "+yd) ; x=1.0f ; xd=1.0 ; y=Short.MAX_VALUE ; // 16 ビット整数の最大値を float 実数に 14 yd=Short.MAX_VALUE ; z=x/y ; zd=xd/yd ; // 16 ビット整数の最大値を double 実数に // /は除算を意味する System.out.println("z is "+z) ; System.out.println("zd is "+zd) ; } } 結果は以下のようである。 base is 10 y is 1.0 yd is 1.0 z is 3.051851E-5 zd is 3.051850947599719E-5 小数点の小数点以下の精度が float と double で大きくことなることがわかる。 2.1.3 演算子 演算子として以下のものがある。全部を覚える必要はまず今のところないが、知っている、知 っていないで後々プログラミング技術で差がでてくる場合もある。 • 算術演算子 • 関係、条件演算子 • シフト、論理演算子 • 代入演算子 • その他 以下に詳しい。 Java Tutorial HP http://java.sun.com/docs/books/tutorial/java/nutsandbolts/opsummary.html 算術演算子 算術(数値)計算でのみ有効 演算子 使い方 + op1 + op2 説明 op1 と op2 の和 * op1 - op2 op1 * op2 op1 から op2 を引く op1 × op2 / % op1 / op2 op1 % op2 op1 ÷ op2 op1 を op2 で割ったあまり(剰余) ++ ++op op の値を利用する前に op の値を1増加 ++ -- op++ --op op の値を利用した後に op の値を1増加 op の値を利用する前に op の値を1減少 + +op もし op が byte, short, or char なら、int として扱う - -op op 値を負にする 関係、条件演算子 15 2つの数(変数、定数)のさまざまな関係を計算する演算子(結果は真 (true) か偽 (false)のみ) 演算子 使い方 真を返す場合 > >= op1 > op2 op1 >= op2 op1 は op2 より大 op1 は op2 以上 < <= == op1 < op2 op1 <= op2 op1 == op2 op1 は op2 より小 op1 は op2 以下 op1 と op2 は等しい != op1 != op2 op1 と op2 は等しくない && || op1 && op2 op1 || op2 op1 と op2 ともに true, op2 を条件的に計算 op1 あるいは op2 は true, op2 を条件的に計算 ! & ! op op1 \& op2 op は false op1 と op2 はともに真, つねに op1 と op2 を計算 | op1 | op2 ^ op1 ^ op2 op1 と op2 どちらかは真、つつねに op1 と op2 を計算 もし op1 と op2 が異なるとき–つまりどちらかが真でどちらかがか らなずそうでない場合 シフト、論理演算子 演算子 使い方 説明 >> << op1 >> op2 op1 << op2 op1 のビットを op 2個分右にシフト op1 のビットを op 2個分左にシフト >>> op1 >>> op2 op1 のビットを op 2個分右にシフト (符号なし) & | op1 & op2 op1 | op2 ビットごとの and ^ ~ op1 ^ op2 ~op2 ビットごとの xor ビットごとの or ビットごとの complement 代入演算子 基本は以下の形 op1 = op2 ; これ以外に以下のショートカットがある。 演算子 使い方 それはつまり += -= op1 += op2 op1 -= op2 op1 = op1 + op2 op1 = op1 - op2 *= /= %= op1 *= op2 op1 /= op2 op1 %= op2 op1 = op1 * op2 op1 = op1 / op2 op1 = op1 % op2 &= |= op1 &= op2 op1 |= op2 op1 = op1 & op2 op1 = op1 | op2 ^= <<= >>= op1 ^= op2 op1 <<= op2 op1 >>= op2 op1 = op1 ^ op2 op1 = op1 << op2 op1 = op1 >> op2 >>>= op1 >>>= op2 op1 = op1 >>> op2 他の演算子 Java はさらに以下の演算子を利用する。 16 演算子 使い方 ?: op1 ? op2 : op3 [] type [] [] type[ op1 ] [] op1[ op2 ] . op1.op2 () op1(params) (type) (type) op1 new new op1 instanceof op1 instanceof op2 2.1.4 説明 もし op1 が true なら op2 を、そうでなければ op3 を 戻り値とする 長さ未定配列の宣言, 型を含む 長さ op1 の配列を作る、new 演算子とともに用いなけ ればならない 配列 op1 の op2 番目を利用、op2 は 0 から配列長-1 ま でが範囲 オブジェクト op1 のメンバー op2 を参照 パラメータ params を持った method op1 の宣言か読 み出し キャスト (変換) op1 を type に. new オブジェクトか配列を作る. op1 はコンストラク タか配列名 もし op1 が op2 のインスタンスであれば true をもどす 変数、演算子などのプログラム例題 以下はまず 5 個の値の平均値を計算してつぎにひとつデータを加えて 6 個の平均値を計算するプログ ラムである(教育的見地からかかれたものでこんなプログラムを書いてはだめだけど)。 • 最初に 45、57、92、24、75 の和をとりそれを平均値にするため個数(5)で割っている。ところが 平均値の値がおかしい。293/5 であるから 58.6 が正しいのに 58.0 となっている。これは整数と整 数の割り算は自動的に整数の結果にまるめられてしまうからである。58 となる。実数(float)の 変数(average)に代入されて 58 は 58.0 となった。 • 正確な値を得ようとするなら実数/整数あるいは整数/実数の計算にさせてやらなければならない。 プログラムでは整数→実数(この型変換をキャスティングというが)を (float)number として行っ ている。 • なお今まで出てきたかもしれないが、System.out.println の()に注目してほしい。文字列と 変数(ここでは変数の型がでうであれ変数が直接カッコ内にある場合自動的に文字列に変換され るが)あるいは変数と文字列を+でつなげている。ストリング(String)型クラスでの演算子+は 文字列と文字列の結合を意味している。いうまでもないがたとえば以下のプログラム断片 String abc,def,xyz ; abc="Pretty "; def="Boy" ; xyz=abc+def ; では String 型変数 xyz には”Pretty Boy”という値が含まれることになる。 • sum += 86 という計算、number++という計算に注意。 class myAverage { public static void main(String args[]) { int number,sum ; float average; // まず 5 個の値の和 number=5 ; sum=45+57+92+24+75 ; average=sum/number ; System.out.println("Number= "+number+" Sum = "+sum+" Average = "+ average ); 17 // // キャスティング average=sum/(float)number ; System.out.println("Number= "+number+" Sum = "+sum+" Average = "+ average ); // // もう 1 個データを増やして平均再計算 sum += 86 ; number++ ; average=sum/(float)number ; System.out.println("Number= "+number+" Sum = "+sum+" Average = "+ average ); } } 以下のプログラム例は都立大の学修番号から入学年度、学部、類、個人連番号を割り出そうとするも のである。現在のところ 1. 上位 2 桁で入学年度西暦の下 2 桁を 2. 次の 1 桁で学部・課程を(1 から 5 で 5 学部,8 で修士、9 で博士、0 はその他) 3. その次の桁は類・研究科。0∼4 あるいは 7 が A 類、5∼6 あるいは 8 が B 類 4. その次の 2 桁は個人用の連番号で, 5. 最後の 1 桁はチェックデジットで何の意味も持っていない番号である。計算方法はあるらしい。 この情報をもとにたとえば >> java myNumber 0456124 と入れるとその素性を明らかにしてくれるのが以下の myNumber.java プログラムである。さまざま な演算が行われている。プログラムを追いながらそれら演算子の使い方を学んでほしい。 class myNumber { public static void main(String args[]) { int studentnumber,year, rui, gakubu, number ; char charrui ; // 都立大学学修番号の分析(学部生のみ) studentnumber = Integer.parseInt(args[0]) ; studentnumber = studentnumber/10 ; year = studentnumber/10000 ; year += (year<10)? 2000:1900 ; studentnumber = studentnumber % 10000 ; gakubu=studentnumber/1000 ; studentnumber=studentnumber%1000 ; rui = studentnumber/100 ; charrui= ((rui<5)||rui==7)? ’A’:’B’ ; number = studentnumber%100 ; System.out.print ("Entrance Year: "+year) ; System.out.print (" Gakubu number: "+gakubu) ; System.out.print (" "+charrui+"-Rui") ; System.out.println(" Number: "+number) ; } } 18 //入学年度 //学部番号 //類 //連番 2.1.5 課題 1. OperatorTest.java 下のプログラムをまず読む。そして出力(プリント出力)がどうなるか予想してみよ。そして次 にこのプログラムを実行させて答えを確認せよ。 public class OperatorTest { public static void main(String[] args) { int i=10 ; int n ; n = i++%5 ; System.out.println("n = " + n) ; n = i%5 ; System.out.println("n = " +n) ; n = ++i%5 ; System.out.println("n = " +n) ; } } 2. BasicsDemo2.java BasicsDemo.java を改造して以下のように最後にもうひとつプリント文を入れて変数 current の 値を印字させたい。コンパイルエラーをなくすにはどうしたらいいだろうか。 class BasicsDemo2 { public static void main(String[] args) { int sum = 0; for (int current = 1; current <= 10; current++) { sum += current; } System.out.println("Sum = " + sum); System.out.println("Current = " +current) ; } } 3. SumInt1Limit.java やはり BasicsDemo.java を改造して Σn i=1 i の計算ができるようにせよ。n を実行時に入力できる ように改造せよ。つまり # java SumInt1Limit 1000 とすれば 1 + 2 + · · · + 1000 までの和を計算するように。そのためにはデータを String[] args で受 け取る。入力データはひとつのみなので args[0] にデータが入力される。このままではしかし数字 入力したといえどもただの文字並びでたとえば”1000”といれただけなのでこの文字列をプログラ ムのなかで整数字として認識させなければならない。それには以下の手法を使う。 int n = Integer.parseInt(args[0]) ; ここでは基本データ型 int の wrapper クラス Integer のもつメソッド parseInt() を用いて文字列を 整数に変換させている。 4. Sum2limits.java 前出の BasicsDemo.java は 1+2+3+‥+10 と計算している。任意の値をふたつ指定(start、last) し、その間の和を求めるように改造しなさい。整数 start、last は java 実行時に指定できるように しなさい(つまり main(String[] args) に値をを引き出す。start は args[0]、last は args[1] とな る)。したがってこのプログラムを以下のように実行させると 11 から 87 までの和を計算すること になる。 19 # java Sum2limits 11 87 最後に結果をプリントしたあと、その結果が正しいかどうかを以下の式を計算させ、その答え sumken をプリントさせることにより確認できるようにしなさい。 sumken = {last × (last + 1) - start × (start - 1)}/2 5. SecretCalc.java 以下のプログラム断片はどんな計算をしているか考えなさい。できたらその考えを裏付けるため にこの前後を整え正しい java プログラムにさせて実行させなさい。 int answer = 1 ; int index = 1 ; for(int i=0;i<loop;i++) { answer *= index++ ; } なおこの前には loop に値を入力できるように int loop = Integer.parseInt(args[0]) ; をいれなさい。また後には answe rをプリント出力できるようにしなさい。loop の値は 15 以下 に抑えてください。 6. ConeVolume.java 以下のプログラムは実行時クラス名(DiscArea)指定の後に円の半径(その単位は任意 mm でも cm でも m でも Km でもいい)を入力させその面積を計算するものである。 public class DiscArea { public static void main(String[] args) { double r = Double.parseDouble(args[0]) ; double s = Math.PI * r * r ; System.out.println("Disc area of radius "+ r +" = "+ s) ; } } これを参考に底面の円の半径(r)と高さ(h)を入力させ円錐の体積(v)を求めるプログラ ム ConeVolume.java を作りなさい。このプログラムの注意点を挙げておこう。 • java 実行時に受け取ったデータは文字列(例えば 4.5678 はただの文字の並びでありこの時点 で実数 4.5678 に結び付けられていない)である。 • これを Double クラスの parseDouble メソッドが実数 4.5678 と実数に変換している。 • Math.PI は π (円周率)である。 • この実行は例えば# java DiscArea 1.0 とすると半径 1.0 の円の面積を計算し標準出力に Disc area of radius 1.0 = 3.14159265358979 としてくれるものである。 • したがって 2 つの変数 r と h をとり r=1、h=3 の円錐の体積を計算するには例えば # java ConeVol 1 3 とすればいいだろう。 20 7. HourMinSec.java java 実行時にパラメータひとつ入れる。例えば 10058。この入力された整数を 秒数と考え何時間何分何秒であるか計算するプログラムをつくれ。なお args[0] 文字列の整数変 換は以下のようである int s = Integer.parseInt(args[0]) ; 8. RenBunsu.java 最後に考え方はとてもむずかしいけれど、プログラムはとても簡単にできる問題。 以下の無限正則連分数を計算するプログラムを作りなさい。繰り返し数 loop を始めに与えその繰 り返し後のこの連分数の値をプリントする。プログラムの構造は課題 3 と酷似している。answer の計算式を考え付くことと answer は double にしなければならないことに注意。 1+ 1 1+ 1 1+ 1 1+ 1+ 1 1 1+··· 答えは以下のようになるはず。 loop 式 1 2 1 + 11 1 + 1+1 1 1 1 + 1+ 1 1 1 3 answer 値 1+ 4 1+ 2.0 1.5 1.6666.... 1 1 1+ 1+ 1.6 1 1 1+ 1 1 2.2 式、文およびブロック プログラム中の単位としておおざっぱにいって式<<文<<ブロック の関係がいえる。 2.2.1 式(Expressions) 式とは単一の値を計算するたの変数、演算子、メソッド呼び出しを含めた系列(シリーズ)。前回変 数、変数名の議論のときに掲げた MaxVariablesDemo.java をもう一度以下に示そう。 class MaxVariablesDemo { public static void main(String args[]) { // integers byte largestByte = short largestShort int largestInteger long largestLong = Byte.MAX_VALUE; = Short.MAX_VALUE; = Integer.MAX_VALUE; Long.MAX_VALUE; // real numbers float largestFloat = Float.MAX_VALUE; double largestDouble = Double.MAX_VALUE; // other primitive types char aChar = ’S’; boolean aBoolean = true; // display them all System.out.println("The largest byte value is " +largestByte); 21 System.out.println("The largest short value is " + largestShort); System.out.println("The largest integer value is " + largestInteger); System.out.println("The largest long value is " + largestLong); System.out.println("The largest float value is " + largestFloat); System.out.println("The largest double value is " + largestDouble); if (Character.isUpperCase(aChar)) { System.out.println("The character " + aChar + " is upper case."); } else { System.out.println("The character " + aChar + " is lower case."); } System.out.println("The value of aBoolean is " + aBoolean); } } このプログラムにはまだ学習していないことが含まれている。それは • if () { ‥ } else { ‥ } if () { ‥ } else { ‥ } はコントロール文で項でくわしく取り扱うが、いわゆる条件判定のためのイ フ文というもので()内の式が真であればすぐ次の {・・} で囲まれたブロックを、そうでなく偽であ れば else 後の {・ ・} ブロックを実行せよというもの。例えば char aChar=’S’; というところを char aChar=’s’; としてみよ。upper case とは大文字、lower case とは小文字を意味する。 このプログラムのなかで出てくる式について以下にまとめてみた。 aChar = ’S’ 文字型(char)変数に文字「S」を代入 "The largest byte value is " + largestByte "The largest byte value is " という文字列と 整数型変数 largestByte の値の変換1 ) 文字列の結合 Character.isUpperCase(aChar); isUpperCase というクラス Character のメソッドを呼ぶ Java では単純な 1 項、2 項演算式からその組み合わせの複合形までさまざまな式を組み立てることが できる。しかしその場合式を構成している変数パートの各部分で型が一致していなければならない。つ まり char 型の変数と実数型の変数を組み合わせても意味がなく、エラーとなる。複合式を表現すると き、型のマッチング以外に式の評価優先順位を考慮しなければならない。たとえば今 x,y,z が同じ型で あるとして x*y*z はどう書いても結果に影響はない(つまり x*z*y、x*(y*z) と表記しても)。しかし以下の式 x + y /100 は表記としてあいまいさが残る。Java としては厳密に優先順位が規定されているが、それがプログラム した人の考えと一致するとは考えられない。そこでプログラマーは自分のアイデアを厳密に反映させる ために (x+y)/100 とか x+(y/100) 1 [注意] 文字列”1234”と計算結果の数字 1234 はコンピュータとしてはまったくべつの表現に属するもの。したがって計算 結果をプリントさせるためにはその数字を「文字化」させなければならない。 22 と表示させるべきである。演算子にはそれぞれ相対的な優先順位があり、それは今までの数学の常識に かなったものであるが、ものによっては注意を要するものもある。以下の表はもっとも優先度の高い演 算子から降順に並べてある。かっこでくくる場合その中の式の評価の優先順位が上がるが Java では式 に使われるかっこはすべて () であり{[{()}] の区別は行わない。 名称 記述 接尾演算子 [] . (params) expr++ expr-++expr --expr +expr -expr ~ ! new (type)expr 単独演算子 creation あるいは cast 乗除 * / \% + - 加減 シフト << >> >>> < > <= >= instanceof == != 関係 等号 ビット毎 AND ビット毎排他的 OR ビット毎 OR 論理 AND 論理 OR 条件 && || ? : = += -= *= /= \%= &= ^= |= <<= >>= >>>= 代入 2.2.2 & ^ | 文(Statements) 文は実行の最小単位となるものである。以下のような式が文となる。なお文はセミコロン;で終了。 • 代入式 • ++か–を使った式 • メソッド呼び出し • オブジェクトのコンストラクター(クリエーション)式 それぞれの例として aValue = 8933.234; //代入文 aValue++; System.out.println(aValue); Integer integerObject = new Integer(4); //増加文 //メソッド読み出し //オブジェクト作成 さらにもう2つ。それは宣言文(declaration statement)で変数名を宣言するもの。たとえば char aChar = ’s’ ; (文字型変数 aChar を宣言し、その変数を初期化させsという文字を代入させる、とか float aValue = 8933.234 ; それともうひとつは次節に述べるコントロール文である。コントロール文は文の実行順序を制御する。 今まで見たのは for 文とか if 文。 23 2.2.3 ブロック(Blocks) 文をいくつかまとめたもので { 文 1; 文 2; ‥ 文n } と中カッコでくくる。これは単文がおけると ころならどこにでもおける。たとえば int aModulo, aNumber, bNumber, workNumber ; ‥‥ if(aModulo != 0) { workNumber = aNumber ; aNumber = bNumber ; bNumber = workNumber ; } else { System.out.println(".......") ; } では if 文の条件があうときは3つの単体文を実行させたいので3文をカッコでくくりブロック化させ ている。それ以外では(else)単文であるがではさみブロック化させている。 int sum = 0; int sum2 = 0; for (int current = 1; current <= 10; current++) { sum += current; sum2 += current*current ; } これは既出例の BasicsDemo.java の一部を改変している。この例では繰り返し for のくりかえしが 10 2 その後の{}全体に適用される。その結果このプログラムは Σ10 i=1 i と Σi=1 i をひとつの for ループ後に計 算させることができる。なおこの例でブロック化を忘れた場合 int sum = 0; int sum2 = 0; for (int current = 1; current <= 10; current++) sum += current; sum2 += current*current ; この直後に sum、sum2 はどのような値を保持しているだろうか? 2.2.4 課題 1. 初期に i=10 として i--%5 > 0 はどのような値になるだろうか。そして i の値は最終的にどうなるだろうか、確認するプログラム を以下の書き出しを参考に記述せよ。i=11 ではどうか? ExpState.java class ExpState { public static void main(String[] args) { int i = 10 ; ‥ ‥ } } 2. 2つの実数変数 xVaklue と yValue がある。プログラムの最初に xValue=12.345 で、yValue=54.321 であったとしよう。この値を入れ替えるプログラムを代入文を使って記述せよ。 ExChange2.java 24 class ExChange2 { public static void main(String[] args) { double xValue=12.345, yValue=54.321 ; System.out.println("初期:xValue and yValue = " + xValue + "," + yValue) ; ‥ ‥ ‥ System.out.println("最後:xValue and yValue = " + xValue + "," + yValue) ; } } 2.3 コントロール文 コントロール文をふくまないプログラムを書くとコンピュータはプログラムの上から下に順番に文を 実行していくようになっている。しかしそのような単純な順序だての計算はまれでたいがいは条件によ るプログラム部分の選択的実行や、ある部分の繰り返しが含まれるプログラムができあがる。このよう な実行順序を改変する文をコントロール文とよぶ。こまかくいうとコントロール文には4種類あるがこ こでは以下の3種類をみることにする。それで十分であろう。 文 Java キーワード 繰り返し while, do-while, for 決定 if-else, switch-case break,continue,label:,return 分岐 2.3.1 while、do-while 文、不特定回数の繰り返し while 文は以下の形をとる。 while(expression) { statement(s) ; } expression をまず計算、この expression の型は boolean でなければならない。その値が真(true)であ るかぎりその後の { ‥ } を繰り返す。その計算過程でやがて expression が真ではなくなり偽(false)にな るであろう。そのときこの繰り返しはおわり { ‥ } のあとに続く文が実行される。したがって while(true) などと書いてしまうとそれは無限ループを意味することになってしまいプログラムは永久に終わらなく なる。 このような例の考えてみよう。データをいくつかとり統計処理をする。このときデータの個数はその 時々でちがう。ある場合は 100 個のデータ、あるときは 23 個とか。しかしその個数にかかわらずデータ がこれ以上ないという時点で入力を終了しデータ解析(たとえば平均値をとる)を行いたい。 より具体的にみるためあるクラスのテストの平均値の計算とする。この場合0点はいるがマイナスの 点をとる学生はいないとする。そこでデータ入力する際通常の成績入力が終わったとき、End-Of-Data マークとして-1 を入れるようにする。そうすると上のような統計処理プログラムは以下の構造となるだ ろう。 以下の例は 3 桁の整数すべての各桁の数の和をとるプログラムである。そのうちその和が 11 になる 場合もとの3桁の数をプリントさせるプログラムである。 WhileDemo.java 25 class WhileDemo { public static void main(String[] args) { int mil=1000 ; int ketasum = 0 ; int hundred,other,ten,one ; int index=100 ; while(index<mil) { hundred = index/100 ; other = index%100 ; ten = other/10 ; one = other%10 ; ketasum = hundred+ten+one ; if(ketasum==11) System.out.println("ketasum for "+index+" is 11") ; index++ ; } } } do-while 文は以下の一般形をとる。 do { statement(s) } while (expression) ; この do-while 文も不特定回数の繰り返しである。この繰り返しの継続条件はしかしループの最後にお こなわれるため、最初に必ず1回 { ‥ } は実行させられる。以下に例を示す。これは上記の例と変わら ないが3桁の整数の各桁の和を求めその和の 25 倍がもとの整数と等しいものがあるかどうかのサーチ を行う。while との繰り返しの継続条件の計算位置がちがうので繰り返しの継続条件がすこしちがって いることに注意。 DoWhileDemo.java class DoWhileDemo { public static void main(String[] args) { int factor = 25; int mil = 1000 ; int ketasum = 0 ; int hundred,other,ten,one ; int index = 100 ;、 do { hundred = index/100 ; other = index%100 ; ten = other/10 ; one = other%10 ; ketasum = hundred + ten + one ; if(ketasum*factor == index) System.out.println("Check Number: "+index) ; index++ ; } while(index <= mil) ; } } 2.3.2 課題 1. 3 乗と2乗の差が 200 より大きい最小の整数値を求めるプログラム。Int32200.java 整数型変数 x の初期値を1から始めて x3 − x2 ≤ 200 が成り立っている間は x の値を1つふやす。 成り立たなくなった時点で上記の条件を満たす最小値に達したということでその値を印字させて おわり。do-while を使ってください。 26 2. 計算機 (イプシロン)(精度)を求める。CalcEpsilon.java ある実数 x は計算機では x = a1 /2 + a2 /4 + a3 /8 + a4 /16 + .... + an /(2n ) と表される。a1 から an は1か0である。n の値を知れば計算機の実数の精度がわかる。これを調 べるプログラムを作ってみよう。それは 1 + 1/2, 1 + 1/4, 1 + 1/8, 1 + 1/16, ..........., 1 + 1/2n と計算させて 1 + を求める。いつか有限精度から 1 = 1 + となる。この時繰り返しをやめ、そ の一歩手前のεを求める。 2.3.3 for 文による繰り返し、特定回数の繰り返し コンパクトに規定回数の反復を行うのに非常に便利である。 とくにベクトル型変数(配列)との組み合わせでおおきなプログラミングパワーを発揮する。 for 文は以下の形式である。 for(初期設定、終端条件、増加設定) { 文(複数)} 初期設定は繰り返しに先立ち一回初期化を行う、その設定をする。終端条件は繰り返しの終了の条件を 各繰り返しの先頭で計算、その式が真であればループを継続、そうでなければループは終わる。増加設 定は各ループの最後に実行される。これらの3つの設定は省略することも可能。for( ; ; ) は無限ルー プを意味することになる。 TempConv.java public class TempConv { public static void main(String[] args) { int Low=-20, High=40 ; float Factor=1.8f, Bias=32.0f ; // 温度単位 摂氏と華氏の変換表 System.out.println("Temparature C to F Table") ; for (int degree = Low; degree < High; degree++) { System.out.print(degree+" C --- ") ; System.out.print((int)(Factor*degree+Bias)+ " F\t") ; if(degree%2 !=0 ) System.out.println() ; } } } 2.3.4 課題 1. 以下の図形の作図 DrawTriangle1.java * ** *** **** ***** ****** ******* ******** ********* ********** 27 2. 以下の図形の作図 DrawTriangle2.java * *** ***** ******* ********* *********** ************* *************** ***************** ******************* 2.4 配列 1つの変数名のもとで1つの値しか保持しない変数(スカラー)、以下の図でいうと i とか sum、と1 つの変数名で多数の値を保持できるもの、配列(Array)とがある。配列は多数の値の区別を番号(イ ンデックス、0、1,2,3、‥)をもっておこなう。この番号を添字という。添字は 0 以上で負の数は 許されない。以下の図でいう vector[0] から vector[9] 参照。配列のサイズは配列.length でプログラム で取得できる。 配列の定義、配列へのデータ入力 • 初期値をともなう配列の定義の仕方 int varAar[]={0,1,2,4,8,16,32,64,128,256} ; 整数(4 バイト)10 個の領域を確保。 • 初期値をとらずに配列を定義 I double celsius[] = new double [36] ; 8 バイト長の実数で 36 個の領域を確保、new により配列の各要素は zero になる。 • 初期値をとらずに配列を定義 II char a[] ; a = new char[20] ; 20 バイトの領域確保。 • [] の中に添字(index)をいれて、配列の添字番目の要素を操作できる。ここでは a の 3 番目の要 素に’c’ を入れていたり、摂氏の温度データが入っている配列 24 からから華氏へ温度の単位を変 換し配列 Fahrenheit の 911 番目に入力している。 a[3]=’c’ ; Fahrenheit[911]=Celsius[24]*1.8+32 ; 配列と for( ; ; ){....}を組み合わせてたくさんのデータの一括入力、一括処理が簡単にできる。 以下の例は一括処理。 28 public class ForDemo { public static void main(String[] args) { int[] vector = { 32, 87, 3, 589, 12, 1076, 2000, 8, 622, 127 }; int sum = 0 ; for (int i = 0; i < vector.length; i++) { System.out.print(vector[i] + " "); sum += vector[i] ; } System.out.println(); System.out.println("Vector sum: "+ sum) ; } } public class ArrayDemo { public static void main(String[] args) { int lenData=50 ; long number[] = new long [lenData] ; long even[] = new long [lenData] ; long odd[] = new long [lenData] ; long fibonacci[] = new long [lenData] ; long irand[] = new long [lenData] ; double frand[] =new double [lenData] ; for (int i = 0; i < lenData; i++) { number[i]= i ; even[i]=2*i ; odd[i]=2*i+1 ; fibonacci[i]=(i<2)? 1:fibonacci[i-2]+fibonacci[i-1] ; // [0,1) の一様乱数発生(詳しくは§4 class Math 利用を参照) frand[i]=Math.random() ; long dum =(long)(frand[i]*(double)Long.MAX_VALUE) ; irand[i]=dum%100 ; //irand は [0,99] までの整数の一様乱数 } for(int i=0;i<lenData; i++) { System.out.print(number[i]+" ") ; System.out.print(even[i]+" ") ; System.out.print(odd[i]+" ") ; System.out.print(irand[i]+" ") ; System.out.print(fibonacci[i]+" ") ; System.out.print(frand[i]+" ") ; System.out.println() ; } } } 2 次元配列 上のプログラムは 2 次元配列という行列形式の配列を使えばさらにデータ処理をコンパクト化できる。 public class Array2Demo { public static void main(String[] args) { int lenData=50 ; long overall[][] = new long [lenData][5] ; double frand[] =new double [lenData] ; for (int i = 0; i < lenData; i++) { overall[i][0]= i ; overall[i][1]=2*i ; overall[i][2]=2*i+1 ; overall[i][3]=(i<2)? 1:overall[i-2][3]+overall[i-1][3] ; // [0,1) の一様乱数発生(詳しくは§4 class Math 利用を参照) frand[i]=Math.random() ; long dum =(long)(frand[i]*(double)Long.MAX_VALUE) ; 29 overall[i][4]=dum%100 ; //irand は [0,99] までの整数の一様乱数 } for(int i=0;i<lenData; i++) { for(int j=0;j<5; j++) System.out.print(overall[i][j]+" ") ; System.out.print(frand[i]+" ") ; System.out.println() ; } } } 2.4.1 課題 1. 前節例題を改変し平均値、標準偏差をもとめるプログラムを作成、データは 32,73,25,57,34,90,68,83,45,76 とする。 • 平均値はすべてのデータの和を個数で割った実数 • 標準偏差はすべてのデータの2乗の平均値と上で求めた平均値の2乗の差の平方根 2. 循環水送り問題 JunkanBaketsu.java N 人の人が両手にバケツを持ち、円周上に並んでいる。N 人に対し以下の2つの繰り返しを行う。 (a) みんな右手のバケツに入っている水の半分を、左手のバケツに移す。 (b) 左手のバケツの水を全部、左隣の人の右手のバケツに加える。N 番目の人の左バケツは 0 番 目人の右バケツに移す。 この 2 回の繰り返しをステップと呼ぼう。最初に 0 番目の人の右手のバケツに1リットルの水が 入っていて、他のバケツは空だとする。ステップを繰り返し毎ステップごとの各自の右手(ある いは左手の)バケツの水量を表示するプログラムを作成する。このとき参加人数 N とステップ繰 り返し回数は最初に入力指定。 考え方 i 番目の人の右手のバケツの水量を right[i]、左手のを left[i] とする。先の水送り操作は (a) left[i] = right[i]*0.5 ; right[i] = right[i]*0.5 (0<=i<n) (b) right[i] = right[i]+left[i-1] (1<=i<n), right[0] = right[0]+left[n-1] public class JunkanBaketsu { public static void main(String[] args) { float right[] = new float[20] ; float left[] = new float[20] ; int i ; // 初期設定 (n は人数、r は繰り返し総数) int n=10 ; int r=250 ; for(i=0;i<n;i++) { right[i]= (float)0.0 ; left[i]= (float)0.0 ; } right[0] = (float)1.0 ; System.out.println("Initial") ; for(i=0;i<n;i++) { System.out.print(right[i]+" ") ; } System.out.println() ; 30 //バケツリレーの開始 for(int j=0;j<r;j++) { // 繰り返し段階 I // 繰り返し段階 II System.out.println((j+1)+" times") ; for(i=0;i<n;i++) { System.out.print(right[i]+" ") ; } System.out.println() ; } } } 3. 前節の例を見てみよう。その中に一様乱数 Math.random() を使って 0 から 99 までの整数の一様 乱数の発生をさせる部分があった。この部分を抜き出す。そして乱数発生を 10000 回繰り返す。配 列 ranCount[i] をまずゼロにクリアさせ乱数発生で i がでたら(0 ≤ i ≤ 99)1 カウントを増や す。最終的には ranCount には 0 から 99 までの乱数の出現回数がそれぞれ格納されている。それ を順番に書き出しください。そしてその平均をとってください。100 種類の整数の一様乱数発生を 10000 回行うので平均は 100 近辺になるはず。できれば標準偏差をとり分布があまりあばれてい ないことを確認してください。 2.5 if 文による条件判断分岐 if 文を用いて選択的にプログラムの一部を実行させたりとばしたりすることができる。たとえばプロ グラムの実行過程で変数xの値をプリントさせたいがいつもそうではなくある変数の値が真であればプ リントさせ、そうでなければスキップさせることができる。 boolean DEBUG = false ; x = .. . . . . . . ; if (DEBUG) { System.out.println("DEBUG: x = " + x) ; } 一般的には 以下のような単純型で書き表すことができる。 if (expression) { statement(s) } ここで expression の計算結果が真の時 {} 内の statements を実行させる、という意味をもたせる。ex- pression の結果が偽であれば {} 部はスキップされ、} の次に続く文が実行される。また expression の結 果が真であるときと偽であるときで異なる文のセットを実行させたいときは if(expression) { statement(s) } else { statement(s) } //expression が真の時 //expression が偽の時 31 もうひとつの形として場合場合が複雑になる場合 if(expression1) { statement(s) //expression1 が真の時 else if (expression2) { statement(s) //expression1 は偽だが,expression2 は真の時 else if (expresison3) { statement(s) //expression1 も expression2 も偽だが expression3 は真の時 else { statements //expression1,expression2,expression3 全部偽の時 } 例題 public class SeisekiBunrui { public static void main(String[] args) { int testscore = 76; char grade; if (testscore >= 90) { grade = ’A’; } else if (testscore >= 80) grade = ’B’; } else if (testscore >= 70) grade = ’C’; } else if (testscore >= 60) grade = ’D’; } else { grade = ’F’; } System.out.println("Grade = { { { " + grade); } } 2.5.1 課題 1. ある鉄道運賃が(1)10Km までは一律 200 円、(2)10∼30Km までは 10Km 越す分について Km あたり 10 円増し、(3)30Km 以上では 30Km を越す分についてさらに Km あたり 5 円増し となっている。目的駅までの距離 k から運賃を求めるプログラムを作る。FareCalc.java 2. 前節の例題は一人の成績について評価するものである。今 10 人の受講生がいたとしてその成績 が int[] testscore = {32,76,85,91,23,51,34,48,60,13}となっていた場合のこの10人の 評価ができるように上の例題プログラムを改造する。for 文を利用する。ClassSeiseki.java 2.5.2 配列を使ったプログラミング課題 1. Ripple.java というプログラムを作る。まず以下のプログラムを読んでください。できれば実行 してください。 public class Ripple { public static void main(String args[]) { int ncell=75 ; int i,j,k,left,right ; 32 int dcell[]=new int [ncell] ; int odcell[]=new int [ncell] ; for(i=0;i<ncell;i++) { dcell[i]=0 ; odcell[i]=0 ; } int ngeneration=ncell ; dcell[0]=1; dcell[1]=2 ; dcell[ncell-2]=2 ; dcell[ncell-1]=1 ; for (i=0;i<ncell;i++) { if(dcell[i]==0) System.out.print(’ ’) ; if(dcell[i]==1) System.out.print(’#’) ; if(dcell[i]==2) System.out.print(’*’) ; } System.out.println(); for(i=0;i<ncell;i++) { odcell[i]=dcell[i] ; } } } さてこのプログラムに続きを作ります。最後のの for 文のあとに(つまり下から2行目と3行目の 間に)以下のシナリオで示す通りのプログラムを付け加えてください。 (a) 以下のことを ngeneration-1 回繰り返す i. 以下のことを i=0 から i<ncell 繰り返す A. 1 世代(generation)前の dcell[i] を参照する。その左の (i-1) の cell 値(left) と右の (i+1)cell 値 (right) を足す。もし i=0 なら左の cell 値は 0(left=0), 同じように もし i=ncell-1 であるなら右の cell 値は 0(right=0)とする。 B. そのときの中央の(i)の cell 値に応じて(それを pivot と呼ぼう)、以下のように次 の世代の cell 値(i)を決める。 • pivot 値が 0 で、left+right 値が 2 以上なら、それは 2 とする、そうでなければ (left+right 値が 2 より小なら)0 とする。 • pivot 値が 1 ならどんな場合でも次世代 cell 値は 0、 • pivot 値が 2 で left 値あるいは right 値が 0 である限り次世代 cell 値は 1、そうで なければ(pivot 値が 2 であっても)次世代 cell 値は 2 ii. 上の例のように cell 値に応じて文字を出力(i=0,i<ncell)、行末に改行を挿入。 iii. cell 配列の世代交代をする。 (b) なにも後始末せずに終了。 2. 以下のような問題をプログラム化してください。voterRule.java 20 人の投票者が独立に、両端の人の意見(0 or 1)を聞いて多数に従う。2 人の意見が分かれてい るときには自分の意見は変えない。これを 5 回繰り返す。ただし端は周期的とする。たとえば最 初に 1 0 1 0 1 0 0 1 0 1 0 1 0 1 1 0 1 0 1 0 だとする。一回ごとのこれらの数値の変化を表示せよ。 3. やはりセルオートマトンの問題です。cellAM.java。 こんどは横一列の配列を以下のように定義 します。 33 int ncell=39 ; int []field = new int[ncell] ; int []nextfield = new int[ncell] ; 最初に field は field[19]=1 以外全部 0 とします。そして以下のようなルールに従って次の世代を考 えます。i=0 から ncell-1 まで, field[i] はその 1 世代前の field[i-1],[i],[i+1] の値で以下のようにさせ ます。 Index field[i-1] field[1] field[i+1] 次世代の field[i] のとるべき値 7 1 1 1 1 6 5 1 1 1 0 0 1 0 1 4 3 2 1 0 0 0 1 1 0 1 0 1 0 0 1 0 0 0 0 0 1 0 1 0 つまり以下の図のように前世代の自分の値と両隣の値が次世代の自分の値になります。各欄は 0 か 1 しかとれないので上の8つの組み合わせが考えられます。 そしてあるパターンの 0,1 の組み合わせで次に 1 か 0 になるかは表の右端に与えられています。こ れを 20 世代繰り返すと以下のような図になります。出力 1 は「1」、0 は「.」で表示させています。 なお各世代 i=0 から i=ncell-1 まで field[i] の値を変えていくのですが i=0 で次世代を考えると きは i=38,i=0 と i=1 の組み合わせ,i=ncell-1 のときは i=ncell-2,i=ncell-1 それと i=0 として考え ます。 ...................1................... ..................1.1.................. .................1.1.1................. ................1.1.1.1................ ...............1.1.1.1.1............... ..............1.1.1.1.1.1.............. .............1.1.1.1.1.1.1............. ............1.1.1.1.1.1.1.1............ ...........1.1.1.1.1.1.1.1.1........... ..........1.1.1.1.1.1.1.1.1.1.......... .........1.1.1.1.1.1.1.1.1.1.1......... ........1.1.1.1.1.1.1.1.1.1.1.1........ .......1.1.1.1.1.1.1.1.1.1.1.1.1....... ......1.1.1.1.1.1.1.1.1.1.1.1.1.1...... .....1.1.1.1.1.1.1.1.1.1.1.1.1.1.1..... ....1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.... ...1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1... ..1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.. .1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1. 1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1 .1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1. 34 また上の表で index=2 のときの次世代 field[i] を1とさせるだけで以下のように図は変化します。 ...................1................... ..................111.................. .................1.1.1................. ................1111111................ ...............1.11111.1............... ..............111.111.111.............. .............1.1.1.1.1.1.1............. ............111111111111111............ ...........1.1111111111111.1........... ..........111.11111111111.111.......... .........1.1.1.111111111.1.1.1......... ........1111111.1111111.1111111........ .......1.11111.1.11111.1.11111.1....... ......111.111.111.111.111.111.111...... .....1.1.1.1.1.1.1.1.1.1.1.1.1.1.1..... ....1111111111111111111111111111111.... ...1.11111111111111111111111111111.1... ..111.111111111111111111111111111.111.. .1.1.1.1111111111111111111111111.1.1.1. 1111111.11111111111111111111111.1111111 111111.1.111111111111111111111.1.111111 これらを出力するプログラム cellAM.java を作ってください。出力はどちらかひとつでかまいませ ん。しかし 1ヶ所かえるだけで両方ちゃんと出力できるか確認してください。 ややっこしいのは 3 個の field の値を比べるところです。以下のようにしてみたらどうでしょうか。 int i1=i-1 ; int i2=i+1 ; if(i1<0) i1+= ncell ; if(i2>38) i2 -= ncell ; index=0 ; if(field[i1]==0) { if(field[i]==0) { if(field[i2]==0) index=0 ; else index=1 ; } else { if(field[i2]==0) index=2 ; else index=3 ; } } else { if(field[i]==0) { if(field[i2]==0) index=4 ; else index =5 ; } else { if(field[i2]==0) index=6 ; else index=7 ; } } 2.6 //0,0,0 //0,0,1 //0,1,0 //0,1,1 //1,0,0 //1,0,1 //1,1,0 //1,1,1 参考 Web ページ 今まであまり省みなかったがそれでもいくつかの基本算術関数(sin , cos , log , √ )などが定義されて いるクラスがある。どのような関数が定義されているか、あるいはそれらの Java プログラムからの利 用の仕方については http://java.sun.com/products/jdk/1.2/docs/api/java/lang/Math.html(英文) を参照してほしい。 35 数値計算と class Math の利用 3 3.1 class Math の変数、メソッド class Math は Java Development Kit (JDK) が提供する共用 class で数学的定数を class 変数とし て、数学関数を class メソッドとして用意している。 class Math は public class Math として定義されている。class 定義の前に public をつけているの で、class をインスタンス化(class を他の class が利用するとき具体的な変数として名前付けすること) なしにこの class の中で定義されている変数、メソッドを利用できる。以下にこの class で定義されてい る定数、メソッドをまとめた。なおこの class(public class)の変数、メソッドを利用するときは Math. という接頭辞を必ずつける。実数で戻り値があるメソッドはすべて型が double であることに注意。 定数 E PI 型 double double 記述 自然対数の底 e 円周率 π 戻り値型 メソッド名 abs(double) double int long ○ ○ ○ 絶対値 ○ ○ 絶対値 ○ 絶対値 ○ abs(float) abs(int) abs(long) 記述 float 絶対値 acos(double) asin(double) atan(double) ○ atan2(double,double) ceil(double) ○ cos(double) exp(double) floor(double) ○ ○ cos 指数関数 exp 小数点値切り上げ(double で戻す) log(double) max(double,double) ○ 自然対数 ○ 2つの数値の最大値 ○ min(int,int) min(long,long) pow(double,double) random() rint(double) round(double) round(float) sin(double) sqrt(double) tan(double) (x,y) → (r, φ) のφ 小数点値切り下げ(double で戻す) ○ ○ ○ max(float,float) max(int,int) max(long,long) min(double,double) min(float,float) arc cos arc sin arc tan ○ 2つの数値の最大値 ○ 2つの数値の最大値 ○ ○ 2つの数値の最大値 2つの数値の最小値 ○ 2つの数値の最小値 ○ 2つの数値の最小値 ○ 2つの数値の最小値 ○ pow(a,b)=ab ○ 0.0 から 1.0 までの一様乱数値 引数の四捨五入値 ○ ○ ○ 引数の四捨五入値 引数の四捨五入値 ○ sin ○ 平方根 ○ tan 36 3.2 Math を使ったさまざまな例 3.3 反復的に方程式 f (x) = 0 の解を求める 3.3.1 はじめに まず表題の議論に入る前に計算精度について確認しておこう。以前の課題の計算機精度εについてで も考察したが実数には有限の精度がある。これは実数型 double と float で違う。たとえば 1/10 を 10 回、 1/100 を 100 回、1/1000 を 1000 回足すという計算を 1/1000000000 まで繰り返そう。すべてにわたっ て当然結果は1になるべきだが。以下にそのプログラムと計算結果を示す。ここで class Math の pow というメソッドが利用されていることに注意。 class powerSum { public static void main(String args[]) { float fsum,fx ; double dsum,dx ; int i,k,n ; for(k=1;k<9;k++) { n=(int)Math.pow(10.0,(double)k) ; fx=1.0f/n ; fsum=0.0f ; dx=1.0/n ; dsum=0.0 ; for(i=0;i<n;i++) { fsum += fx ; dsum += dx ; } System.out.println(n+" "+fsum+" "+dsum) ; } } } この結果は以下のようになる。 10 1.0000001 0.9999999999999999 100 0.99999934 1.0000000000000007 1000 0.9999907 1.0000000000000007 10000 1.0000535 0.9999999999999062 100000 1.0009902 0.9999999999980838 1000000 1.0090389 1.000000000007918 10000000 1.0647675 0.99999999975017 100000000 0.25 1.0000000022898672 float 型で計算するとすでに n=1000 あたりでも答えの精度の有効数字がずれてきてしまう。double 型 では 1000000000 でもそれなりの計算結果を保持している。このことに注意してほしい。しかし実数計 算の精度を 64 ビットにすれば精度は満足できるかもしれないがプログラムの容量は大きくなるし、実 行速度も遅くなる。あまり精度を気にしない実数計算(たとえばグラフィックスでの座標計算など)で むやみやたらと double で計算させても意味はあまりなくなる。またその他の要因で数値計算の精度が 悪くなる場合もあるので注意しよう。それらは課題で考えていこう。 3.3.2 二分法(はさみうち法) いわゆる科学技術計算ではきれいに f (x) = 0 となる x が求められるような関数の解を求めるのでは なく数値的にでしかxを求めることができない場合が多い。その場合なんらかの方法で反復を繰り返し て求める数値を精度的に満足のいく範囲でもとめることになる。いくつか方法が考案されているがここ では二分法というのを見てみよう。以下の図に示すように関数 f (x) が a と b の間で 1 回だけ符号をか えることがわかっているとする。そのような a,b を初期値として話をすすめる。この場合以下の反復に より f (x) = 0 の解を求めることができる。 37 初期値 a(f(a)<0)および b(f(b)>0)を与える、 求める解の精度 d を与える、 |a-b|>d の間、以下{}内を繰り返す { x=a+b/2 y=f(x) もし y>0 ならば b=x とする そうではなく y<0 ならば a=x とする } x が求める解であるとしてその値をプリント これに基づいて f (x) = x2 − r = 0 をとなる x を求めてみよう。以下のプログラムである。とうぜ √ ん解は (r) である。ここで実数 r として適当な正の実数値を入れてみるのだが以下のことを考慮して 1/4 < r ≤ 1 の範囲のみを考えてみる。それは適当な整数 m を用いて r を r = 4m · r (1/4 ≤ r ≤ 1) と表すことができるからで √ r = 2m √ r となるのでコンピュータを使って平方根計算をするには 1/4 < a ≤ 1 の範囲のみ実数計算させあとは整 数計算を優先させるほうが速いからだ。ここでは r = 1/4 としてみよう。結果はもちろん 0.5 である。r の範囲を限定させれば初期値範囲も a=0,b=1 と固定できる。なお以下のプログラムでは中間結果をみ たくて余計なプリント文を入れている。 class binSolve { public static void main(String args[]) { double a,b,x,y,d,r ; int index=0 ; a=0.0 ; b=1.0 ; d=1.0e-7 ; x=1.0 ; r=0.25000000004 ; while(Math.abs(a-b)>d) { x=(a+b)*0.5 ; y=x*x-2.0 ; if(y>0) b=x ; else a=x ; System.out.println("index = "+index+" x= "+x) ; index++ ; } 38 System.out.println("Final answer= "+x+" after "+index+" repeats") ; } } 結果は以下のようであった。a=0,b=1 なので今の場合偶然第1回目の繰り返しでいきなり正解に達し ているが a,b の差が大きく while ループの条件は満たされるのでさらに繰り返しは続いていく。 index index index index index index index index index index index index index index index index index index index index index index index index Final 3.3.3 = 0 x= 0.5 = 1 x= 0.75 = 2 x= 0.625 = 3 x= 0.5625 = 4 x= 0.53125 = 5 x= 0.515625 = 6 x= 0.5078125 = 7 x= 0.50390625 = 8 x= 0.501953125 = 9 x= 0.5009765625 = 10 x= 0.50048828125 = 11 x= 0.500244140625 = 12 x= 0.5001220703125 = 13 x= 0.50006103515625 = 14 x= 0.500030517578125 = 15 x= 0.5000152587890625 = 16 x= 0.5000076293945312 = 17 x= 0.5000038146972656 = 18 x= 0.5000019073486328 = 19 x= 0.5000009536743164 = 20 x= 0.5000004768371582 = 21 x= 0.5000002384185791 = 22 x= 0.5000001192092896 = 23 x= 0.5000000596046448 answer= 0.5000000596046448 after 24 repeat Newton-Raphson 法 関数の曲線が以下の図のように解付近で単調になっているなら関数の勾配(導関数)を使うことによ り効率的に解を見出すことができる。 精度dを与えておく。初期値 x(n=0) も与えておく。 |x(n + 1) − x(n)|/x(n + 1) > d の間、以下の{}の計算を繰り返す。 { 1. 現在点 (x(n), f (x(n)) における関数の接線の傾き f (x(n)) を計算 39 2. y − f (x(n)) = f (x(n))(x − x(n)) が x 軸と交わる点を計算、次の点すなわち x(n + 1) とし、こ れを計算。 x(n + 1) = x(n) − [f (x(n))/f (x(n))] } 精度が満たされ反復繰り返しを終えたら x(n + 1) を解としてプリント。 以下にプログラムを示す。収束の様子を調べたかったのでここでも while ループのなかにプリントを 入れてみた。平方根をとるべき数を実行時に入力させたかったので double の wrapper クラス Double の ValueOf メソッドを利用した。そのため変数 rr を Double クラスとしてインスタンス化した。class Double の rr の値を double 型に変換させなければならずやむなく doubleValue という class Double の メソッドを利用した。前回初期値として2つの値 a、b を必要とした。今回は初期値は x の出発点であ る。固定して x=1 にする。 class newtonSolve { public static void main(String args[]) { double d,xold,xnew,r ; int index=0 ; Double rr ; rr=Double.valueOf(args[0]) ; d=1.0e-7 ; xold = 2.0 ; xnew = 1.0 ; r=rr.doubleValue() ; while(Math.abs(xnew-xold)/xnew > d) { xold = xnew ; xnew = (xold+r/xold)/2.0 ; index++ ; System.out.println("index= "+index+" xnew="+xnew) ; } System.out.println("Final x = "+xnew+" after "+index+" repeats") ; } } 結果は以下のようである。xold=2. 0を最初に入れているがこれはまったく任意の値でいいが、以下 に続く while ループの条件を満たすようにする。収束状況は前回の二分法に比べて格段とよくなってい る(なぜだろうか?)。 index= 1 xnew=0.6250000002 index= 2 xnew=0.512500000356 index= 3 xnew=0.5001524394232112 index= 4 xnew=0.5000000236305733 index= 5 xnew=0.5000000004000005 Final x = 0.5000000004000005 after 5 repeat 3.4 数値積分とメソッドの自作 いまは定積分を完全に数式の変形により(求められる場合は)求めてしまうアプリケーションプログ ラムもあるが、やはり素早くどのような関数でも普遍的にその定積分が求められる数値積分は大変便利 である。 定積分は結局面積の計算ということなので2次元の関数 y=f(x) と積分の上限、下限と y=0 で囲まれ る図形を効率的に面積を求められやすい長方形や台形に近似させるということが出発点になる。 以下に定積分数値計算を ∫ 1 A= 0 4 dx = π 1 + x2 40 を元に考えてみよう。 以下の図は x が 0 から1までの上記の関数の振る舞いである。0 ≤ x ≤ 1 を均等区間割しその区間内 の関数の面積を左は四角形で、右は台形で近似したものである。いうまでもなく関数の性質によるだろ うがなめらかな関数は区間の幅をうまく取れば台形近似で定積分を精度良く求められるだろうことがわ かる。 以下のプログラムは台形近似計算プログラムである。このなかに今まで慣れ親しんできた main メソッ ド以外に static double func(double x) {...} static double intrap(double a, double b, long ndivide) {...} というのがある。 これは自分でこのクラスにメソッドを加えたものである。最初の func は戻り値が double であり、引数 として()内で double の値をとっている。y = f (x) に対応している。戻り値は return(y) で指定され る。さらに static という修飾語もあるがとりあえず今必要のない概念なので忘れておく。statuc main から呼び出される場合この static という修飾語を必要とする。今はだから必ずつけておく。 つづいて intrap というメソッドは3個の入力引数を必要としている。doube 変数で a と b、long 変数 で ndivide である。これは func の [a, b] の定積分値を計算するものである。ndivide は [a, b] の区分けの 数である。 メソッドは通常計算を引数の値を変えて何回も計算したいとき、あるいはなにか普遍的な計算機能と して独立にさせておきたいときに使う。 このプログラムは数値計算にともなう誤差をはっきり強調させるためすべて 32bit の実数計算(float) でおこなった。 class trapezoidInt { static float func(float x) { float y ; y=4.0f/(1.0f+x*x) ; return(y) ; } static float intrap(float a, float b, long ndivide) { float h, area ; area = 0.0f ; h=(b-a)/ndivide ; for(int i=0;i<ndivide-1;i++) { float left=a+i*h ; float right=left+h ; area += (func(left)+func(right))*0.5 ; 41 } area *= h ; return(area) ; } public static void main(String args[]) { long N ; for(int k=3;k<21;k++) { N=Math.round(Math.pow(2.0f,(float)k)) ; float area = intrap(0.0f,1.0f,N) ; System.out.println("N = "+N+" Area = "+area+" "+area/Math.PI) ; } } } 結果は以下のようになる。これでは区間を細分化させれば π 値に近づいていくが、さらに細分化させ すぎるとかえって数値計算にともなう誤差が積まれていきかえって精度が落ちていくことがわかる。 N N N N N N N N N N N N N N N N N N = = = = = = = = = = = = = = = = = = 8 Area = 2.8723955 0.9143118895951402 16 Area = 3.0119133 0.9587217795785631 32 Area = 3.0779376 0.9797379680907292 64 Area = 3.1100562 0.9899616229133766 128 Area = 3.1258965 0.9950037444496709 256 Area = 3.1337624 0.9975075400174159 512 Area = 3.1376815 0.9987550361238234 1024 Area = 3.1396375 0.9993776458121655 2048 Area = 3.1406147 0.9996887229833642 4096 Area = 3.1411057 0.999844982533376 8192 Area = 3.1413474 0.9999219359980247 16384 Area = 3.1414688 0.9999605645123306 32768 Area = 3.14153 0.9999800684969606 65536 Area = 3.1415596 0.999989478979817 131072 Area = 3.1415389 0.9999828764636194 262144 Area = 3.1415486 0.9999859879942413 524288 Area = 3.1412358 0.9998864190143406 1048576 Area = 3.140424 0.9996280101907419 3.5 乱数発生とシミュレーション さていままで多少硬い話が続いたのでちょっと直感的な話題を考えてみよう。それは乱数(random number)を使ったものである。この乱数の用途は多岐にわたるがよく使われる代表は確率過程のシミュ レーションである。たとえばギャンブルは 0 か 1、あるいは 1 から 6 までの数字を使うダイス、さらに多 くの数字を使うルーレットなどで一般的には出た所勝負である。確率過程である。このシミュレーショ ンに乱数が使われるのはくどく説明するまでもないだろう。 3.5.1 ダイスゲーム 次のプログラムをみてもらおう。class Math の random() というメソッドを使って整数の 0 から最大 整数まで出現する整数乱数を完成させそれを 6 で割ってあまりをとっている。6 である整数を割れば剰 余は 0 から 5 のまでの 6 通りとなりその数に 1 を足してダイスシミュレータが完成される。 つづいて main メソッドでは Ntotal のダイスをふらせ出た目のカウントを行っている。安全のため最 初の 20 回は出た目も横 1 列に書きださせている。プログラムを以下に示す。 class diceOne { static int dice() { int irand = (int)(Math.random()*(double)Integer.MAX_VALUE) ; return(irand%6+1) ; } 42 public static void main(String args[]) { int Ntotal=3600 ; int value[] = {0,0,0,0,0,0} ; for(int i=0;i<Ntotal;i++) { if(i<20) System.out.print(dice()+" ") ; for(int j=1;j<7;j++) if(dice()==j) value[j-1]++ ; } System.out.println() ; for(int j=0;j<6;j++) { int jj=j+1 ; System.out.println("Dice(" +jj+"): "+value[j]+"/"+Ntotal) ; } } } この結果(の 1 例)は以下のようである。この乱数を用いた計算は毎回毎回このようになるとは限らない。 大体統計的変動内において数は一致するが正確には一致しない。それは乱数が毎回毎回 Math.random() 関数を呼ぶたびに違う値を指し示すからだ。計算機上では乱数は擬似乱数と呼ばれ厳密にランダムな数 ではなくある数列に基づいて生成される。ということはもしいつもいつも初期値が同じであれば毎回同 じ乱数数列が生成されるはずである。この class Math の random() はその悪弊を廃し、初期値を適当な 数(今の時刻の分秒の整数値とか)に設定させて見かけ上は常にでたらめな数が出るように工夫されて いる。 5 4 5 4 3 2 1 5 5 6 2 2 5 2 3 3 1 5 3 1 Dice(1): 607/3600 Dice(2): 581/3600 Dice(3): 572/3600 Dice(4): 577/3600 Dice(5): 603/3600 Dice(6): 574/3600 3.5.2 石ころなげゲーム:的に当たる数は? 次はたとえば1 m × 1m 四方の板上に何個も何個も小石を投げつけるゲームのシミュレーションを行 うことを考えよう。1回ごとにぶつかった点の詳細な座標 x,y を記録しておく。最後に集計するときそ √ の座標から x2 + y 2 の値を計算しその値が 1m 以下であれば数を数える。そして全試行数のなかでそ の数がどのくらいあったかを計算させる。その数をかく。また回数を増やして同じことを繰り返す。た とえば 4000 粒の小石を投げて先の条件計算させることを 1 ゲームとする。10 ゲームでの平均値を求め てみよう。1 ゲームでだいたい 3142 個が条件に合致すると期待されるが。 図示すると以下のようなことをやっているわけである。 43 このプログラムは以外と簡単にあっさりと書き下せる。もともとシミュレーションとは物語性が高い ものなのでプログラムの書き出しもストーリーにしたがっていけばいいのかもしれない。 class MCpiCalc { public static void main(String args[]) { long Ntotal=4000 ; long sum ; double x,y,r,average ; long game=10 ; long inside ; sum=0 ; for(int k=0;k<game;k++) { inside = 0 ; for(int i=0;i<Ntotal;i++) { x=Math.random() ; y=Math.random() ; r=Math.sqrt(x*x+y*y) ; if(r<=1.0) inside++ ; } System.out.println("Game: "+k+" = "+inside) ; sum += inside ; } average=(double)sum/game ; System.out.println() ; System.out.println("Average over "+game+" games = "+average) ; } } 結果は以下が 1 例である。pi を求めるのにはシミュレーションはあまりよい方法とはいえないかもし れない。 Game: Game: Game: Game: Game: Game: Game: Game: 0 1 2 3 4 5 6 7 = = = = = = = = 3152 3105 3109 3141 3178 3172 3109 3162 44 Game: 8 = 3102 Game: 9 = 3165 Average over 10 games = 3139.5 3.5.3 3.6 ゲームの理論:囚人のジレンマ 課題 45 アプレット(Applet)を使って図形を描く 4 本来であればここから Java の真骨頂で面白くなってくるところ。残念ながら時間があまりない。図 形は Appletviewer か Netscape(IE でも)で表示可能なようにするため Applet という概念を利用して描 く。Java で Applet プログラムを作りその結果を Appletviewer か Netscape で鑑賞するやり方もここで 学ぶ。とくに注意してほしいのは Applet を Web ブラウザで表示するには Applet を記述する Java プログラムと Applet を Web ブラウザに載せる方法を記述する Web の html ファイルの2つが必要 であるということである。 4.1 画面構成 基本的にスクリーン 2 次元平面を使って図形を描くのであるから、まずその基本となる画面構成を理 解しよう。以下に示すのがその画面座標である。 [x,y]=(0,0) x height width y (width,height) 図中x座標末端を意味する width やy座標末端の height の値は Java プログラムにおいては以下に例示 するが、Applet クラスのメソッド init で指定する。 4.2 4.2.1 Class Graphics を使った基本作図 基本図形要素描出 applet プログラム例 以下の Java プログラムを例示することにより基本図形作図プログラミングを見ていこう。 Java プログラム(DisplayShape.java) import java.applet.Applet ; import java.awt.* ; public class DisplayShape extends java.applet.Applet { public void init() { resize(600,600) ; } public void paint(Graphics gobj) { int r,g,b ; Color clr,fg,bg ; clr = Color.black ; //new Color(r,g,b) ; gobj.setColor(clr) ; int x,y,rectWidth,rectHeight ; // 直線 (drawLine) x=10; y=140; rectWidth=140; rectHeight=-140; gobj.drawLine(x,y,x+rectWidth,y+rectHeight) ; // 多直線 (drawPolyline) 46 int x2point[] = new int[6] ; int y2point[] = new int[6] ; double [] degAngle = {18.0,90.0,162.0,234.0,306.0} ; double starRadius=75.0 ; int jban[]={2,0,3,1,4} ; int xoffset=75,yoffset=225; for(int i=0;i<5;i++) { x2point[jban[i]]=(int)(starRadius*Math.cos(Math.toRadians(degAngle[i]))+0.5)+xoffset ; y2point[jban[i]]=(int)(starRadius*Math.sin(Math.toRadians(degAngle[i]))+0.5)+yoffset ; } x2point[5]=x2point[0] ; y2point[5]=y2point[0] ; gobj.drawPolyline(x2point,y2point,x2point.length) ; // 塗りつぶし多角形 (fillPolygon) int xPoints[] = {70,20,35,120,150} ; int yPoints[] = {310,340,430,430,390} ; gobj.fillPolygon(xPoints,yPoints,xPoints.length) ; // 多角形 (drawPolygon) int x1Points[]={75,15,135} ; int y1Points[]={472,576,576} ; gobj.drawPolygon(x1Points,y1Points,x1Points.length) ;) // 円あるいは楕円 (drawOval) x=160; y=10; rectWidth=130; rectHeight=130; gobj.drawOval(x,y,rectWidth,rectHeight) ; //塗りつぶし円あるいは楕円(fillOval) y=180; rectHeight=90; gobj.fillOval(x,y,rectWidth,rectHeight) ; //角丸四角(drawRoundRec) y=320; rectHeight=110; int arcWidth,arcHeight ; arcWidth=30; arcHeight=30; gobj.drawRoundRect(x,y,rectWidth,rectHeight,arcWidth,arcHeight) ; //塗りつぶし角丸四角(fillRoundRec) x=180; y=460; rectWidth=90; rectHeight=130; arcWidth=50; arcHeight=50; gobj.fillRoundRect(x,y,rectWidth,rectHeight,arcWidth,arcHeight); //四角(fillRect、drawRect、draw3DRect、fill3DRect) bg = Color.lightGray ; fg = Color.black ; for(int i=0;i<4;i++) { x=310+i*10 ; y=30-i*10+i*150 ; rectWidth=130-i*20 ; rectHeight=90+i*20 ; if(rectHeight > 150) rectHeight=145 ; if(i==0) { gobj.fillRect(x,y,rectWidth,rectHeight) ; } else if (i==1) { gobj.drawRect(x,y,rectWidth,rectHeight) ; } else if (i==2) { gobj.setColor(bg) ; gobj.draw3DRect(x,y,rectWidth,rectHeight,true) ; } else { 47 gobj.setColor(bg) ; gobj.fill3DRect(x,y,rectWidth,rectHeight,true) ; } } //弧(drawArc) gobj.setColor(fg) ; int arcRadius=120 ; x=460-arcRadius; y=140-arcRadius; rectWidth=arcRadius*2; rectHeight=rectWidth; int startAngle, extendAngle ; startAngle=30 ; extendAngle=60 ; gobj.drawArc(x,y,rectWidth,rectHeight,startAngle,extendAngle) ; //塗りつぶし弧(fillArc) arcRadius=70; rectWidth=arcRadius*2-10; rectHeight=rectWidth+20; x=525-rectWidth/2 ; y=225-rectHeight/2 ; startAngle=120 ; extendAngle=60; gobj.fillArc(x,y,rectWidth,rectHeight,startAngle,extendAngle) ; } } この java プログラムを使って applet を作り上げるのだが、この applet を appletviewer か Web ブラ ウザに表示させるには applet 表示用 html ファイルを作成しなければならない。 DisplayShape.htm <html> <body> Applet Test:<p><P> <applet code="DisplayShape.class" width=600 height=700> </applet> </body> </html> これら2つのファイルを以下の手順で実行させた。 [id@machine~]# javac DisplayShape.java [id@machine~]# appletviewer DisplayShape.htm この結果を以下に示す。 48 この図は applet に作図するクラス java.awt.Graphics のほとんどの基本作図要素を網羅している。以下 の表に上図内の各要素を作図するのに必要な class Graphics のメソッドを示している。 詳しくは http://java.sun.com/j2se/1.4.1/docs/api/java/awt/Graphics.html を参照のこと。 4.2.2 drawLine drawOval fillRect drawArc drawPolyline fillOval drawRect fillArc fillPolygon drawRoundRect draw3DRect drawPolygon fillRoundRect fill3DRect DisplayShape.java の分析 I Applet クラスの継承、Graphics クラスの具体化 • import java.applet.Applet ; import java.awt.* ; java.applet に属する Applet クラス(applet を作成するため、また applet 内に配置したコミュニ ケーションツール処理のためのクラスライブラリ)と java.awt に属するすべてのクラス(ユーザ インターフェース、グラフィックスおよびイメージ描画に必要なクラスライブラリ)を用いること を宣言する。 • public class DisplayShape extends java.applet.Applet { public void init() { resize(600,600) ; } public void paint(Graphics gobj) { より Apple tクラスを継承して自分自身で DisplayShape という独自 applet を定義する。Applet クラスにはさまざまなメソッドが用意されているがこの DisplayShape ではとくにメソッド init と メソッド paint を改編している。init では applet のサイズ(グラフ表示座標範囲)を設定、paint メソッドは java.awt.Graphics クラスを利用してイメージや図形を記述させるようにしている。 49 • public void paint(Graphics gobj) { int r,g,b ; Color clr,fg,bg ; clr = Color.black ; //new Color(r,g,b) ; gobj.setColor(clr) ; paint ではまず Graphics クラスを具体化(オブジェクト化)したクラス変数 gobj を使う。Graphics クラス定義の変数、定数、メソッドをオブジェクト gobj を通して利用していく。したがって基本 作図要素描画メソッドを呼び出すときには接頭辞”gobj.”をその名前のまえに必ずおく。次に Color クラスをオブジェクト化し変数名を clr,fg,bg で宣言。clr には Color クラス変数を利用して黒を登 録。Graphics のメソッド setColor で現在の前景色を黒に設定。 4.2.3 DisplayShape.java の分析 II Graphics クラスの基本作画メソッド 直線 drawLine(x1,y1,x2,y2) 四角 drawRect,fillRect,draw3DRect,fill3DRect(x,y,width,height,{boolian raised}) 左上コーナーの座標と四角の幅(width)、高さ(height)を指定。draw3DRect、fill3DRect は true か false でエンボスが表面から盛り上がるか、下がるかを指定。 楕円、円(oval) drawOval,fillOval(x,y,width,height) 4つのパラメータは xxxxRect の最初の4つのパラメータと同様。 角丸四角 drawRoundRect,RoundRect(x,y,width,height,arcWidth,arcHeight) 最初の4つのパラメータ(引数)指定は通常の四角を描く場合と同じ。それに以下の図に示した arcHeight 角の曲りを指定するパラメータを加える。 arcWidth 50 多角形、ポリゴン drawPolygon,fillPolygon(int[] xPoints, int[] yPoint,ints nPoints) 各頂点の座標を配列形式でx、y個々に設定、最後にポイント数を指定。 (x[1],y[1]) (x[5],y[5]) NP=6 円あるいは楕円弧(arc) drawArc,fillArc(x,y,width,height,startAngle,extend) 弧の中心は x,y と width、height で指定さえる四角の中心になる。startAngle、extend は弧の初期 角度と角度幅。ともに度(degree)で指定。初期角度=0 °とは時計 3 時を意味する。そして角度 は反時計周りで増えていく。 startA ng 4.2.4 height width extend θ 0 色指定 4.2.1 節の例題 DisplayShape.java にも多少例が載っているが線、面の色指定について見ておこう。基 本となるのは Color クラスをオブジェクト化するために変数を用意することである。例えば Color clr,lineclr,areaclr などと指定する。そしてそれらの変数に色値を設定する。その方法は2 つある。 1. Color クラスにすでにクラス変数として指定されている規定値を利用すること。 clr=Color.black ; lineclr=Color.red ; areaclr=Color.lightGray ; 2. 自分で赤、緑、青の配合を考えて色を作りだすこと。 Color clr ; int red,green,blue ; red=125; green=200 ; blue=64 ; clr= new Color(red,gree,blue) ; 各色値は 0 から 255 まで。 そしてその後に setColor メソッドを呼び出すことで次に再び setColor メソッドが呼ばれるまであらゆ るグラフィックス操作の色は現状の指定値を元におこなわれる。 public void paint(Graphics g) { int r=200,g=100,b=120 ; Color clr,fg,bg ; clr = new Color(r,g,b) ; g.setColor(clr) ; 51 クラス Color については http://java.sun.com/j2se/1.4.1/docs/api/java/awt/Color.html を参照のこと。 4.2.5 文字列 Graphics クラスとして applet に文字列を表示するのも他の基本図形描画と変わりない。しかし文字 列は描画の前に色(設定しなければそれ以前に指定されたもの)、フォント(同様)、サイズ(同様)、あ るいはスタイル(同様)を設定しなければならない。つまりテキストの描画は以下の drawString メソッ ドで行われるのだが、そのフォント、大きさ、色、スタイルはその時点での値を採用するということで ある。 drawString(String, int x, int y) さらにテキストの開始点を x,y で指定するのだが、この y 値は以下の図でいうと Baseline を基準に指定 しなければならない。したがって下部にもまだ成分が残っている文字(g とか y)についての配慮も必 要である。上部も大文字に対しても十分余裕をみてスペースを確保しなければならない。 A scender L ine Hj B aseL ine Descender L ine 例:DisplayString.java import java.awt.* ; public class DisplayString extends java.applet.Applet { public void init() { resize(400,200) ; } public void paint(Graphics g) { Font f = new Font("TimesRoman",Font.PLAIN,14) ; g.setFont(f) ; g.drawString("The quick brown fox jumps over the lazy dogs back",10,100) ; } } その html ファイル:DisplayString.htm <html> <body> <applet code="DisplayString.class" width=400 height=200> </applet> </body> </html> 実行結果は 52 4.2.6 Font について 前節で文字列表示についてもっとも一般的な drawString メソッドを使った方法を概説した。本節では そのときちょっと触れた文字表示の設定(フォント、スタイル、サイズ)について触れてみたい。Graphics クラスのメソッドに setFont というメソッドがあり以後に利用される文字フォントの設定をする。文字 フォントは Font クラスの変数として実現される。Font クラスの実現は Font というクラスコンストラク タによりなされるが、そのために必要なパラメータはフォントの名前、スタイル、サイズである。具体 的には前節に例示した DisplayString.java の以下の部分を参照してほしい。Font クラスコンストラ クタの各パラメータのどれがどの意味をもっているかは明白であろう。 public void paint(Graphics g) { Font f = new Font("TimesRoman",Font.PLAIN,14) ; g.setFont(f) ; Font コンストラクタは Font(String name, int style, int size) ; の形で設定される。 name フォント名で実際のフォントセットの名前か Dialog,DialogInput,Monospaced,Serif,SanSerif,Symbol で示されるいわゆる論理名称を利用する。フォントセット名には以下の例で示すような TimesRoman,Helvetica,courier などがある。 style スタイルは Font.PLAIN,Font.ITALIC,Font.BOLD で指定。 Font.BOLD|Font.ITALIC 指定もできる。 size ポイントサイズ。整数。 以下に setFont/drawString のプログラム例 DisplayFonts.java とその実行例を示す。 import java.awt.* ; public class DisplayFonts extends java.applet.Applet { public void init() { resize(600,400) ; } public void paint(Graphics g) { Font f1=g.getFont() ; String s = f1.getName()+" "+f1.getSize()+" (Default)" ; g.drawString(s,10,10) ; Font f2 = new Font("TimesRoman",Font.PLAIN,10) ; 53 s = f2.getName()+" "+f2.getSize() ; g.setFont(f2) ; g.drawString(s,10,100) ; Font f3 = new Font("Helvetica",Font.PLAIN,12) ; s = f3.getName()+" "+f3.getSize() ; g.setFont(f3) ; g.drawString(s,10,150) ; Font f4 = new Font("courier",Font.PLAIN,14) ; s = f4.getName()+" "+f4.getSize() ; g.setFont(f4) ; g.drawString(s,10,250) ; Font f5 = new Font("DialogInput",Font.PLAIN,36) ; s = f5.getName()+" "+f5.getSize() ; g.setFont(f5) ; g.drawString(s,10,300) ; } } 4.3 参考 Web ページ 今まで出てきたクラスに関する解説 web サイトを以下に示そう。 1. Applet クラスは http://java.sun.com/products/jdk/1.2/docs/api/java/applet/Applet.html 2. Graphics クラスは http://java.sun.com/products/jdk/1.2/docs/api/java/awt/Graphics.html 3. Color クラスは http://java.sun.com/products/jdk/1.2/docs/api/java/awt/Color.html 4. Font クラスは http://java.sun.com/products/jdk/1.2/docs/api/java/awt/Font.html 5. なおさらに進んだ基本(2 次元)図形描画、処理クラスも存在する。それは Graphics2D クラスで あり、 http://java.sun.com/products/jdk/1.2/docs/api/java/awt/Graphics2D.html 54 4.4 課題 1. 以下のような図を作ってください。画面 (width x height) = 600 x 600 です。 2. 以下のような図を作ってください。画面 (width x height) = 600 x 600 です。外枠の四角の大きさ は 576x576、中の四角の辺の長さはバランスをとって適当でいいです。 55 3. 以下のような図を作ってください。画面 (width x height) = 600 x 600 です。上記課題パターンが 縦横 8 個づつ並んでいます。 56 4. 以下のプログラムは円を drawLine のみで描くプログラムである。よく読んでほしい。Graphics ク ラスのメソッドはすべて整数は int、実数は float なので double → float 変換(cast)や、実定数の 与え方(1.0f、0.25f など)に注意を払ってほしい。また Math クラスの数学関数(メソッド)や定 数 (Math.PI)などもみな double なのでその結果を利用する場合も cast しなければならない。 DsiplayCircle.java import java.applet.Applet ; import java.awt.* ; public class DisplayCircle extends Applet { float PI=(float)Math.PI ; int winWidth=300, winHeight=300 ; float circRadius=100.0f ; public void init() { setBackground(Color.lightGray) ; setForeground(Color.red) ; resize(winWidth,winHeight) ; } public void paint(Graphics g) { float r=circRadius,delta,theta,x0,y0,x1,y1 ; float xc,yc ; int nPoints =200 ; xc=winWidth/2 ; yc=winHeight/2 ; x0=0.0f ; y0=0.0f ; for(int k=0;k<nPoints;k++) { if(k==0) { theta=0.0f ; x0=r*(float)Math.cos(theta)+xc ; y0=r*(float)Math.sin(theta)+yc ; } theta=2.0f*PI*(float)(k+1)/(float)nPoints ; x1=r*(float)Math.cos(theta)+xc ; y1=r*(float)Math.sin(theta)+yc ; g.drawLine((int)x0,(int)y0,(int)x1,(int)y1) ; x0=x1; y0=y1; } } } この結果は以下のようになる。 このプログラムを熟読してください。そして以下に示すプログラムを作ってください。 57 DisplayLeaves.java 実数 a=固定(circRadius)にして、半径 r を theta(θ)とともにを以下の ように変える. r = a sin (jθ/i) ここで i,j は整定数、a は実定数(circRaius)。0 ≤ θ ≤ 2π 。つまり r は θ とともに変化する。 i,j をいくつか変えたサンプル出力を以下に示す。 プログラムは 1 ≤ i ≤ 5, 1 ≤ j ≤ 5 すべてに完璧に対応できるようにしてください。また最後の文 字列は以下のようにコーディングしました。 g.drawString("Leaves i= "+i+" j= "+j,110,275) ; なおできれば以下のような一覧表を作成してみてください(これは必ずしも必須ではない、余裕 のある人は挑戦してください)。文字の位置は移動しました。 58 5. 以前みたセルラーオートマトンの一般化したものを作成します。ある与えられた正の整数数 init (0 から 255)を 2 進数で表現するプログラムをまず作る。二進数とは整数を init = n7 · 27 (128) + n6 · 26 (64) + n5 · 25 + · · · + n1 · 21 (2) + n0 · 20 (1) に分解し係数 n7 から n0 まで求め、これらを n7 n6 · · · n1 n0 と並べて表すことである。各係数は 0 か 1 のみをとる。たとえば 78 は 01001110、170 は 10101010 となる。そしてこの係数をプリント するとともに整数配列 bitpat[8] に格納する。bitpat[0] は n0 、bitpat[1] は n1 といったように。 さてここにパターンとして 11011000101... といったような 1 と 0 のみの数字列が並んでいるとす る。そしてこのパターンがある法則で次の世代のパターンを作りだしていくものとする。その法 則はパターン中のある数字の次の世代はその数字と左右の数字の 3 つで決まるものとする。1 か 0 しかない数字が 3 個あるので組み合わせとして全部で 23 = 8 個の法則を導入すればいい。つまり 111 →? 110 →? 101 →? 100 →? 011 →? 010 →? 001 →? 000 →? である。この数字の並びを 2 進数は 10 進数で言うと上から 7,6,5,4,3,2,1,0 である。この法則は先 に init で与えた 0 から 255 の数字の 2 進数の係数で決めることにする。つまり配列 bitpat[i] が i 番目のパターンの次世代を決めるとする。たとえば init=250 は 11111010 なので 111 → 1 110 → 1 101 → 1 100 → 1 011 → 1 010 → 0 001 → 1 000 → 0 となる。いま 61 個の 1、0 の羅列があり最初は真中の 30 番目が 1 でそれ以外は全部 0 となってい るとする。そして init に適当な値をいれて法則を与え 60 ステップ(世代)ほど繰り返した場合の 各数字のパターン変化を観測してプリントしたい。そのようなプログラムを作ってください。望 むらくは 1 は*、0 は空白に変換してプリントするのがよい。たとえば init=22 だといかの図のよ うになる。 59 つづいてこのパターンを applet で描き小さな四角 5*5 程度の大きさで表すようにしてください。そ のとき数字1ではたとえば四角の色は赤、0 はグレーとするといった工夫をします。また数字の羅 列も 61 から 121 程度に拡大しステップ数も 100 ぐらいにしてください。以下に init=22、init=45 のパターンを示します。四角の大きさは 8*8、数字の羅列は 81、世代交代は 60 ステップです。 60 6. 壁紙パターンを作りたい。ひとつの変数(正数)を任意にとることによって自由にパターンを作 り出せるプログラムを作成したい。そのため以下の手順を考えた。 整変数 side に値をセット for i ← 0 to 99 y = i × side/100 for j ← 0 to 99 x = j × side/100 c=整数(x^2+y^2) if c が偶数 色をマゼンタ(magenta)にセット 5 × 5 程度の塗りつぶし四角を (j,i) ポジションに対応するところに描画(fillRect) else 色を明るいグレイ(lightGray)にセット 5 × 5 程度の塗りつぶし四角を (j,i) ポジションに対応するところに描画(fillRect) このレシピにしたがって壁紙パターンを作り出す Java プログラムを作成しなさい。 実際の (j,i) の四角を描く場合図中赤点の位置の計算に注意すること。正方形の幅は5程度が見栄えがす るが変数として異なる値をとれるように考慮すること。 以下にいくつか side の値を変えて作ったパターンを示す。side の値は左から 82、187、357 である。そ の他さまざまな値をとって気に入ったパターンを選んでください。そのときの side の値を知らせてくだ さい。 61
© Copyright 2024 ExpyDoc