11/25 簡単なグラフィクス(2)


11/4の課題について


問題のポイント

エラー処理の方法として,Exceptionを扱った.「間違った数字を入力 した」のはExceptionで処理できるが,「間違った演算子を入力した」のは, 通常の if 文で処理するというのがポイント.

エラー対策は不十分なものが目立った.

10
%
のように,演算子として不適当なものを入力すると,その場でエラーとなって, 再入力を促すことが望ましいが,
10
%
20
のように,数字を入れてから演算子の再入力を促すプログラムもあった.これは,演算子の 種類に応じた処理を一箇所でおこないたいためにこうしたのだと思われる.プログラムの作り やすさという面では有利だが,利用者の立場からすると使いにくい.家電製品などで, 時々このようなインタフェースを持ったものがあるが,見習うべきではない.
普通にプログラミングすると,
  1. 最初の数字を入力.表示
  2. 演算子を入力.「=」なら終了
  3. 数字を入力して,演算子に従って計算し表示.2に戻る
となるが,数字を入力する場所が2箇所になってしまうので,最初に「0+」とい う入力がすでにあるものとして,3からスタートするように書いたプログラムも あった.アイデアとしては面白いが,プログラムの保守性を高めるためには, 元の構造のままにして,数字の入力の部分をメソッド化してプログラムを短く するのが本筋ではある.

解答例(1)

//XXXX XXXX 11月4日出題 電卓プログラム
import java.io.*;//入出力をするために必要
class Kadai1104{

       public static void main(String args[]) throws IOException{

	   //入力をするためには、System.inから
	   //BufferedReaderを作らなくてはいけない
	   BufferedReader d=new BufferedReader
		(new InputStreamReader(System.in));

	   //文字の定義
	   double sum=0.0;
	    int num=0;
	    boolean first=true;

	    //無限ループであるのでループの終了はbreakで行う
	    //演算処理を行う
	    for(;;){
	   
	    String s1;
	    int ch;
	    //はじめは加算を行うことにする
	    if(first==true){
		ch=1;
		first=false;}
	    //演算の種類を決定してもらう
	    else{
		while(true){
		    s1=d.readLine();//キーボードから入力
		       if(s1.equals("+")) ch=1;
		       else if(s1.equals("-")) ch=2;
		       else if(s1.equals("*")) ch=3;
		       else if(s1.equals("/")) ch=4;
		       else if(s1.equals("=")) ch=5;
		       else ch=6;
		       if(ch< 6)
			    break;
		       else System.out.println("次は演算子を入力してください");
		}
	    }
	    
	    //"="が入力されたら終了
	    if(ch==5) break;
	    
	    //数字を入力する
	    while(true){

		String s2=d.readLine();
		try{num=Integer.parseInt(s2);}//例外処理をする
		catch(java.lang.NumberFormatException e){
		    System.out.println("次は数字を入力してください");
		    continue;}
		
		break;}
	
	    //演算を行う
	    switch(ch){
	    case 1:
		sum+=num;
		break;
	    case 2:
		sum-=num;
		break;
	    case 3:
		sum*=num;
		break;
	    case 4:
		sum/=num;
		break;
	}
	    
	    System.out.println(sum);//合計を表示する
	}
	}
   }
演算子を一度数字に直すなど工夫をしています.ただし,このプログラムは数 字の入力として整数値しか仮定していないので,テスト入力は通りません.問題 文のサンプル文でも「0.5」という入力が出て来るので,せめてサンプル入力は 通るようにチェックしましょう.

解答例(2)

//XXXXX XXXX
//はじめに静的メソッドとして、入力された数字を文字列から数字に変換するNum()を作る。
//この中で数字以外のものが入力された時の例外処理を行い、数字が与えられるまで数字の入力を促す。
//数字が与えられるとループから抜けて値を返す。
//次に数字に対するString s と演算子に対するString t、
//演算結果を出すdouble x を設定
//s の初期値 null とし、 x の初期値もだす。
//演算子以外のものが入力されたときのために、演算子による場合分けの部分でループを作る。
//演算子以外のものが入力されたときはこのループを繰り返す。
//1回の演算ごとに演算結果を出力するためにこのループの外側にもう1つループを作る
//"="が入力されたときはプログラム終了なので、外側のループにラベルtoploopをつける。
//x の初期値と演算する数字の2カ所でNumの手順を行うのでメソッドとしてNumを定義した。
import java.io.*;
class Kadai1104{
    public static double Num(String s)throws IOException{
	BufferedReader d=new BufferedReader(new InputStreamReader(System.in));
	double x;
	for(;;){
	    s=d.readLine();
	    try{
		x=Double.parseDouble(s);
	    }
	    catch(java.lang.NumberFormatException e){
		System.out.println("数字を与えてください");
		continue;
	    }
	    break;
	}
	return x;
    }
    public static void main(String[] args) throws IOException{
	BufferedReader d=new BufferedReader(new InputStreamReader(System.in));
	double x;
	String s,t;
	s=null;
	x=Num(s);
	toploop:for(;;){
	    System.out.println(x);
	    for(;;){
		t=d.readLine();
		if(t.equals("=")){
		    break toploop;
		}
		else if(t.equals("+")){
		    x+=Num(s);
		}
		else if(t.equals("-")){
		    x-=Num(s);
		}
		else if(t.equals("*")){
		    x*=Num(s);
		}
		else if(t.equals("/")){
		    x/=Num(s);
		}
		else{
		    System.out.println("演算子 \"+\",\"-\",\"*\",\"/\",\"=\"のいずれかを与えてください");
		    continue;
		}
		break;
	    }
	}
    }
}
入力の一部をメソッド化して簡潔に書いた筋の良いプログラムですが,残念な がらメソッド Num の中でBufferedReaderを作り直しているので,ファイルから の入力の場合はうまくいきません.

解答例(3)

// charset=euc-jp

// XXXXXX XXXX

// コマンドをJavaのdouble型の演算に帰着させています。

// 被演算子に指定された0は、1より十分小さく、-1より十分大きい、0でない数と解釈されます。
// そのため、
// * 0でない数を0で割ると、Infinityになります。
// * 0を0で割ると、エラーとなります。
// * Infinityに0をかけると、エラーとなります。

import java.io.*;

class Kadai1104 {
    
    static double roperand(BufferedReader r) throws IOException {
	for (;;) {
	    try {
		return Double.parseDouble(r.readLine());
	    } catch (NumberFormatException e) {
		System.out.println("被演算子を解釈できません。もう一度:");
	    }
        }
    } // 右被演算子

    public static void main(String[] args) throws IOException {

	BufferedReader r = new BufferedReader(new InputStreamReader(System.in));

	double loperand = 0; // 左被演算子

	for (String operator = "+"; !operator.equals("="); operator = r.readLine()) {

	    // 演算
	    if (operator.equals("+")) loperand += roperand(r);
	    else if (operator.equals("-")) loperand -= roperand(r);
	    else if (operator.equals("*")) loperand *= roperand(r);
	    else if (operator.equals("/")) loperand /= roperand(r);
	    else {
		System.out.println("演算子を解釈できません。もう一度:");
		continue;
	    }
	    System.out.println(loperand);
	}
    }
} 
シンプルで美しいプログラムです.

11/11の課題について


質問と回答

Q.
 XXXXXです。講義のlineをマウスで引くのと同じ感じでarcをかくというのを付け加えてみたのですが

Kadai1111.java:16: Kadai1111 は abstract として宣言する必要があります。mousePressed(java.awt.event.MouseEvent) を Kadai1111 で定義しません。
class Kadai1111 extends Frame implements MouseListener{
^
Kadai1111.java:79: シンボルを解釈処理できません。
シンボル: 変数 arc  
位置    : Kadai1111 の クラス
        g.fillArc(arc[i].here_x,arc[i].here_y,7,7,0,360);
                  ^
Kadai1111.java:79: シンボルを解釈処理できません。
シンボル: 変数 arc  
位置    : Kadai1111 の クラス
        g.fillArc(arc[i].here_x,arc[i].here_y,7,7,0,360);
                                ^

というエラーがでて先に進めません。どこがいけないのか指摘してください、お願いします。




 // AWTを使うので java.awt.*を import する
import java.awt.*;
  // イベント駆動関係のクラスを用いるため
import java.awt.event.*;
import java.io.*;

class Arc{
    public int here_x,here_y;
    public Arc(int x1,int x2,int x3,int x4,int x5,int x6){
	here_x=x1;
	here_y=x2;
    }
}

  // 独立したウィンドウを開くので,Frameクラスのサブクラスにする
class Kadai1111 extends Frame implements MouseListener{
    public Arc[] arcs;
    int arcCount;
  
    int x;
    int y;
    
  public Kadai1111(String title){
    super(title);
    arcs=new Arc[10];
    arcCount=0;
      // GUI部品と,Event Listenerを関連づける
    // KeyAdapterは,KeyListenerを実装して中身は何もないクラス
    // new クラス名(){} で,「クラス名」の名前のない子クラスを定義すると同時に
    // インスタンスを作る
    addKeyListener(new KeyAdapter(){
	    public void keyPressed(KeyEvent e){
		int key=e.getKeyChar();
		System.out.println("keyPressed("+e+","+key+")");
		if(key=='q') System.exit(0);
	    }
	});
  }
    public void mouseClicked(MouseEvent e){
	int mx=e.getX(),my=e.getY();
	System.out.println("mouseClicked("+e+","+mx+","+my+")");
	arcs[arcCount]=new Arc(mx,my,7,7,0,360);
	arcs[arcCount].here_x=mx;
	arcs[arcCount].here_y=my;
	repaint();
    }
    public static void main(String[] args) throws IOException{
	Kadai1111 frame=new Kadai1111("Kadai1111");
	BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
	System.out.println("suuzi");
	String s=br.readLine();
	frame.x=Integer.parseInt(s);
	String n=br.readLine();
	frame.y=Integer.parseInt(n);
	frame.setSize(400,400);
	frame.setVisible(true);    
 

    }
    public void paint(Graphics g){
	int i;
	// インスタンス変数にしたがって描画するコードを書く
	g.setColor(Color.black);
	g.fillRect(0,0,400,150);
	g.setColor(Color.gray);
	g.fillRect(0,150,400,250);
	g.setColor(Color.white);
	g.fillArc(x-10,y+200,140,140,0,360);
	g.fillArc(x,y+130,120,120,0,360);
	g.setColor(Color.black);
	g.fillArc(x+45,y+155,3,10,0,360);
	g.fillArc(x+73,y+155,3,10,0,360);
	g.setColor(Color.yellow);
	g.fillArc(x-50,y,100,100,0,360);
	g.setColor(Color.red);
	g.drawString("A SnowMan And A Moon",250,370);
	
	for(i=0;i< arcCount;i++){
	g.fillArc(arc[i].here_x,arc[i].here_y,7,7,0,360);
	}
    }
}
A.
TAの小田原です。

メールが長くなったので4セクションにわけています。
# われながらやり過ぎかも。長くてすみません;

§1. 後ろ二つのエラーについて
§2. 最初のエラーについて (イベント処理について)
§3. アダプタクラスについて
§4. その他のミス

---------------------------------------------------------------------
§1. うしろ二つのエラーについて
---------------------------------------------------------------------
まず下のふたつは既に気づいているかもしれませんが、arc ではなくて arcs 
という単純なスペルミスです。「シンボルを解釈処理できない」と言われたら、
シンボル(変数名、配列名、メソッド名など)とそのスコープ(有効範囲)を確認
するようにしましょう。

---------------------------------------------------------------------
§2.  最初のエラーについて (イベント処理について)
--------------------------------------------------------------------- 
最初のエラーについては、イベント処理の話になります。エラーの理由は「リ
スナが実装すべきメソッドを実装していなかった」というものです。

クラスがマウスイベントのリスナとして働くためには MouseListener を実装
しなければならないのですが、そうした場合、MouseListenerが宣言している
全てのイベント処理用のメソッドを実装しなければなりません。つまり

mouseClicked
mouseEntered
mouseExited
mousePressed
mouseReleased

という5つのメソッドを、MouseListenerを実装したクラスに書き込まなければ
ならないというわけです。

XXさんのコードでは、mouseClickedのみ実装されており、他の4つが書かれ
ていなかったため、上のようなエラーがでてしまいました。

なおイベント処理用のメソッドの内容は空であっても差し支えありません。
ですから使わないメソッドについては、例えば

void mouseEntered(MouseEvent e){}

などと書くだけで済ませてしまうことが可能です。このようにメソッドの体裁
がととのってさえいればリスナとしての資格は認められます。

さて、とりあえずここまででリスナは用意できたのですが、ここまでちゃんと
やっても、XXさんのコードにはまだ不足しているところがあります。それは
「リスナの登録」です。

リスナに実際に働いてもらうためには、リスナを登録する必要があります。そ
のためには、add○○Listenerというメソッドを使います。

add○○Listener( ○○Listenerのオブジェクト );

とすることによって登録できます。イベントを扱う場合には必ずこのようなメ
ソッドを呼び出すことを忘れないようにしてください。

イベントを扱うときには、

1. イベントを発生させるクラスが存在する
2. イベントを処理するクラス(=リスナ)を実装する
3. イベントを発生させるクラスにおいて、リスナを登録する

という3つが必要になるので、常に確認するようにしてください。ちなみにイ
ベントを発生させるクラスとリスナのクラスは同じクラスであっても構いませ
ん。

イベント処理のコードをゼロから自分で作るのはとても大変なので、最初は授
業で用いたコードや、参考書の簡単なサンプルコードを自分用に改変してみて
使うというのがお勧めです。

---------------------------------------------------------------------
§3. アダプタクラスについて
---------------------------------------------------------------------
使わないメソッドも全て体裁だけは用意しなければならないという手間を防ぐ
ため、アダプタクラスというのが使われます。これは実は授業で扱ったコード
に既に使われています。

    addKeyListener(new KeyAdapter(){
	    public void keyPressed(KeyEvent e){
		int key=e.getKeyChar();
		System.out.println("keyPressed("+e+","+key+")");
		if(key=='q') System.exit(0);
	    }
	});

これはキーイベントを処理するために書かれているものです。

KeyListenerを実装したクラスを書いて、KeyListenerに実装されている全ての
メソッドを自分で書いてやるというのが愚直な書き方ですが、ここではそのプ
ロセスを予めjavaAPIに用意されてあるクラスに任せています。それは
KeyAdapterというクラスです。

上のように、KeyAdapterの中に必要なメソッドだけを定義してやれば、
KeyListenerをimplementsしたクラスを自ら設計することなくイベントを扱う
ことができるようになります。空のメソッドを置く必要はありません。

MouseListenerについても、同様にMouseAdapterというクラスを用いることが
できます。

---------------------------------------------------------------------
§4. その他のミス
---------------------------------------------------------------------
イベント関係以外のミスについてです。

・配列のインデックスarcCountが常に0で動いていません。このため
NullPointerExceptionがでることなどあります。
・また、arcs[] のサイズが10 なので、arcCountが9以上にならないよう注意す
る必要があります。

長くなりましたが以上です。


今日の課題

上のURLは,今日(11/25)の17:00 までは
Forbidden

You don't have permission to access /~ktanaka/programming03/kadai1125.html on this server.
とうメッセージが出てアクセ スできないはずである.17:00以降にも同様のエラーが出る時は,Shiftキーを 押しながら,再読み込み(Reload)を押してみること.

課題の締切りは12/9(火)の21時まで


次へ進む