プログラミング入門(8)


一連の動作

何かをするとき、その中には「決まりきった一連の動作」が含まれることが よくあります。

動作の一例として「リスト1」に「食事をする」動作を書き表してみましょう。

しかし、人間は、「リスト1」のようにいちいち細かいことを考えながら 動いているわけではありません。少なくとも、もっと大きく動作を 捉えていて、「ごはんを食べる」、「ハンバーグを食べる」、 「キャベツを食べる」などのようにまとめて考えているはずです。 これらの部分をそれぞれまとめて「リスト2」に書いてみましょう。

「リスト2」では「食事をする」部分は簡潔でわかりやすくなりました。 しかし全体としてはこれでもまだ長く感じます。 食べ物ごとに食べ方をいちいち分けて書きつらねているので、 同じような文があちこちに現れてしまっています。 「ごはん」だろうが「ハンバーグ」だろうが「キャベツ」 だろうが、食べ方は似ているはずです。 そこで、「『何か』を食べる」の『何か』を Xで表し、 Xを呼び出す側で「何を食べるか」指定して書いてみましょう。 これが「リスト3」です。

X を食べる」動作側では、「呼び出されたときにXが 決まる」という仕掛けです。 すなわち、呼び出され方によって、まず「X←ごはん」となり、 次に「X←ハンバーグ」、そして「X←キャベツ」となる わけです。

これで簡潔に記述できるようになりました。

リスト1
  • 食事をする
    1. 右手を差し出す
    2. 右手で箸をつかむ
    3. 箸でごはんをつまむ
    4. 箸先を口まで移動
    5. 口を開ける
    6. ごはんを口に入れる
    7. 咀嚼する
    8. 箸をつかんだ右手を下ろす
    9. 箸でハンバーグをつまむ
    10. 箸先を口まで移動
    11. 口を開ける
    12. ハンバーグを口に入れる
    13. 咀嚼する
    14. 箸をつかんだ右手を下ろす
    15. 箸でキャベツをつまむ
    16. 箸先を口まで移動
    17. 口を開ける
    18. キャベツを口に入れる
    19. 咀嚼する
    20. 箸をつかんだ右手を下ろす
    21. ... (省略)
    22. 右手から箸をはなす
    23. 右手を引っ込める
リスト2
  • 食事をする
    1. 右手を差し出す
    2. ごはんを食べる
    3. ハンバーグを食べる
    4. キャベツを食べる
    5. ... (省略)
    6. 右手から箸をはなす
    7. 右手を引っ込める
  • ごはんを食べる
    1. 箸でごはんをつまむ
    2. 箸先を口まで移動
    3. 口を開ける
    4. ごはんを口に入れる
    5. 咀嚼する
    6. 箸をつかんだ右手を下ろす
  • ハンバーグを食べる
    1. 箸でハンバーグをつまむ
    2. 箸先を口まで移動
    3. 口を開ける
    4. ハンバーグを口に入れる
    5. 咀嚼する
    6. 箸をつかんだ右手を下ろす
  • キャベツを食べる
    1. 箸でキャベツをつまむ
    2. 箸先を口まで移動
    3. 口を開ける
    4. キャベツを口に入れる
    5. 咀嚼する
    6. 箸をつかんだ右手を下ろす
リスト3
  • 食事をする
    1. 右手を差し出す
    2. ごはん を食べる
    3. ハンバーグ を食べる
    4. キャベツ を食べる
    5. ... (省略)
    6. 右手から箸をはなす
    7. 右手を引っ込める
  • X を食べる
    1. 箸で Xをつまむ
    2. 箸先を口まで移動
    3. 口を開ける
    4. Xを口に入れる
    5. 咀嚼する
    6. 箸をつかんだ右手を下ろす

メソッド

「メソッド (method)」とは、 Java言語のプログラムの中で「一連の動作を行なう部分をひとまとめに して他の場所から呼び出せるようにしたもの」です。 「他のJavaコードから呼び出せる、名前が与えられたJavaの文の集合体」 などと言うこともできます。

メソッドを呼び出すときには、0個以上の引数(「ひきすう」と読みます。 パラメータともいいます)を使って情報を与えます。 メソッドは計算結果を返り値として返します (返り値を返さないメソッドもあります)。


引数の場所には「式」を書くことができます。 「式」の計算結果が、コピーされてメソッドに渡されます。 複数の引数を渡す場合は、引数の間に , (カンマ)を記述します。

System.out.println()メソッド

これまでの授業でも、Java言語のプログラムの中で

    System.out.println("hello world");
などと書いていました。これは実は、このプログラムが 実行されるときに System.out が持つ println() という メソッドの「呼び出し」を行え、という指示だったのです。

上の例では、このメソッドの呼び出しを行なうときに、 "hello world" という文字列(String型のデータ)が パラメータとして渡されています。 (ちなみに、このメソッドは返り値を返しません。)

少し詳しく説明しておくと、

「System クラスの中の out というフィールドは PrintStreamクラスのデータを記憶している。 PrintStreamクラスは println() というメソッドを持っていて、 このメソッドは「パラメータとして渡されたデータを画面に表示する」 という働きをする。そのprintln()メソッドを呼び出した」
というわけです。

上記の説明を自分で調べるためには、 "Java2 Platform, Standard Edition 1.4.0 API仕様" という 日本語の文書を見てみましょう。手近なところでは http://nw.tsuda.ac.jp/doc/j2sdk1.4.0/ja/api/index.html に置かれています。 Webブラウザ (Windowsの場合だと Internet Explorer) でこの文書にアクセスしてみましょう。

  1. アクセスすると、最初の画面は「左上」「左下」「右」という 3つの区画に区切られて表示されています。
  2. スクロールバーを使って「左下」の区画をスクロールさせ、 Systemというクラス名が表示されるようにして下さい。 ABC順に並んでいますので、見付けるのはさほど難しくないはずです。
        ...
        Synthesizer
        SysexMessage
        System      ←これです
        SYSTEM_EXCEPTION
        ...
    
  3. System というクラス名の上でマウスの左ボタンを クリックして下さい。

  4. 「右」の区画に、System クラスの説明が表示されます。 スクロールバーを使って「右」の区画をスクロールさせて、outという フィールドの説明を表示させて下さい。
    フィールドの概要
    static PrintStreamerr   「標準」エラー出力ストリームです
    static InputStreamin   「標準」入力ストリームです
    static PrintStreamout   「標準」出力ストリームです
    という表があります「フィールドの概要」という場所に、 out は『「標準」出力ストリームです』と説明が書かれています。

  5. out フィールドの型は static PrintStream であると書かれています。この PrintStreamをクリックすると PrintStreamクラスの 説明が出て来ます。スクロールさせて「メソッドの概要」の println() の所を観てみましょう。

  6. たくさんの println() メソッドが並んでいることがわかります。
        println()
        println(boolean x)
        ...
        println(double x)
        println(float x)
        println(int x)
        ...
        println(String x)
        ...
    
    println()メソッドを呼び出すときに、渡したパラメータの型によって、 たくさん定義されたprintln()メソッドの中からどのprintln()メソッドが 選ばれるか決まります。上の例では、呼び出しのときに パラメータとして文字列(String型のデータ)が与えられていますので 「println(String x) として定義されているメソッド」が動作します。

Mathクラス

数学関数を集めたクラスが Math クラスです。 上記の "Java2 Platform, Standard Edition 1.4.0 API仕様" で Mathクラスを調べてみましょう。 WEBブラウザの左下の区画で Math を表示させておいて、クリックすると、 右区画にMathクラスの説明が表示されます。

「フィールドの概要」からは PI の説明を、「メソッドの概要」からは abs(), max(), cos(), min(), random(), sin(), sqrt(), tan() などの説明をよく読みましょう。

例えば abs() という「絶対値を返す」メソッドに関しては次のように 4種類のメソッドがオーバーロードされて (=同じメソッド名だがパラメータが異なるメソッドが複数個定義されて) いることがわかります。

メソッドの概要
static doubleabs(double a)   double値の絶対値を返します。
static floatabs(float a)   float値の絶対値を返します。
static intabs(int a)   int値の絶対値を返します。
static longabs(long a)   long値の絶対値を返します。

Mathクラスの abs(), cons() メソッドなどは 型に修飾子として static が宣言されています。このようなメソッドは (クラス・メソッドなので)

   クラス名 . メソッド名(パラメータリスト)
という形で main() メソッドから直接呼び出すことができます。 もしもstaticが宣言されていないメソッドを呼び出すには、一旦そのクラスを new してインスタンス(実体)を生成してから
    クラス名   変数名 = new クラス名();
    変数名 . メソッド名(パラメータリスト)
のようにして呼び出さなくてはいけません。 また、Systemクラスのoutフィールドのようにstaticと宣言されている フィールドは(クラス・フィールドなので)
    クラス名 . フィールド名
のようにして main() メソッドから直接アクセスできます。 staticなフィールドに属するメソッドは
    クラス名 . フィールド名 . メソッド名(パラメータリスト)
のようにして main() メソッドから直接呼び出すことができます。

MSample01.java (差の絶対値を求める)
import java.util.*;
public class MSample01 {
    public static void main(String args[]) {
	Scanner sc = new Scanner(System.in);
	double x, y, z;
	System.out.print("実数を入力して下さい  ");
	x = sc.nextDouble();
	System.out.print("実数を入力して下さい  ");
	y = sc.nextDouble();
	z = Math.abs(x-y);
	System.out.println("差の絶対値は" + z + "です");
    }
}
MSample01の実行例
sp204: ~/pro 4> javac MSample01.java 
sp204: ~/pro 5> java MSample01 
実数を入力して下さい  8.3 
実数を入力して下さい  5.7 
差の絶対値は2.6000000000000005です
sp204: ~/pro 6> 

計算機の中では実数を表すのに、正確に表せない(誤差が生じる) 場合があることに注意して下さい。 上の例では 2.6 という正解ではなく、 2.6000000000000005という若干の誤差を 含んだ結果が表示されています。

MSample02.java (円を描く)
import java.util.*;
public class MSample02 {
    public static void main(String args[]) {
	ToyGraphics tg = new ToyGraphics();
	double theta,dx,dy;
	int i,x,y;
	theta = 0.0;
	while (theta < 2 * Math.PI) {
	    dx = Math.cos(theta) * 200 + 320;
	    dy = Math.sin(theta) * 200 + 240;
	    x = (int) dx;
	    y = (int) dy;
	    tg.drawRGB(x,y,255,255,255);
	    theta = theta + Math.PI/360;
	}
    }
}



演習

課題

GSample05.java では、 2点(sx,sy), (ex,ey) の間に直線を引く際に、 sx==exの場合だけを特別扱いし、それ以外の 場合は基本的に x を動かし、そのxの値を使って y を求めていました。 ところがこの方法では、 直線の視点と終点のx座標の差に比べて y座標の差が大きい場合には、つながった線にはなりません。 たとえば、GSample05.javaに始点(100,100)、終点(150,450) を 与えて線を描かせてみるとよくわかります。


|ex-sx| < | ey-sy| の場合は y を動かしてからそのyの値を使って xを求める方が適切です。


提出課題3

このように直したプログラムを、メソッドとして定義しましょう。

    //直線を(sx,sy)から(ex,ey)まで色(r,g,b)で描くメソッド
    static void drawLine(ToyGraphics tg,int sx,int sy,int ex,int ey,
                                                      int r,int g,int b) {
      // メソッド本体のプログラム
    }

他のメソッド(四角形を塗り潰す、など)の定義も自分で考えて追加してみましょう。

これらのメソッドを呼び出す Mainメソッドを書き、 この drawLine()メソッドを使ったり、 美しい絵を描くプログラムを作成して下さい。

プログラム名は GReport02.java とし、 宿題提出WEB の 「1年セミナー課題3」 http://nw.tsuda.ac.jp/tsuda/handin/up.php?id=2006/1semi/kadai3 から提出して下さい。

〆切は7月31日(月)13:00です。