解答例(1)
//
import java.io.*; // 入力に関するクラスを使う時は必要
class Kadai1022{
// throws IOException で内部で入出力エラーが起きる可能性があることを示す
public static void main(String[] args) throws
IOException{
// 入力をするためには,System.inからBufferedReaderを作らなくてはいけない
BufferedReader d=new BufferedReader(new InputStreamReader(System.in));
double sum=0.0,num=0.0;
// for文の初期化,終了条件,繰り返し部分を省略して書くこともできる
// この場合は, break によって明示的に抜けないと無限の繰り返し
// になる
sum=Double.parseDouble(d.readLine());
System.out.println(sum);
for(;;){
// 1行読み込んで整数に変換する
String s;
s=d.readLine();
//もし=を入力すると終了する
if(s.equals("=")) break;
num=Double.parseDouble(d.readLine());
//足し算か引き算かかけ算かわり算をする。
if(s.equals("+")){
sum=sum+num;
System.out.println(sum);}
if(s.equals("-")){
sum=sum-num;
System.out.println(sum);}
if(s.equals("*")){
sum=sum*num;
System.out.println(sum);}
if(s.equals("/")){
sum=sum/num;
System.out.println(sum);}
}
}
}
講義の範囲内で書かれた素直なプログラムである.ただし,ifが4つ並んでいる
部分は,2つめ以降は else if にしたい.また,
System.out.println(sum);は合流後に書いても良い.殆んどの人はこれと同じようなプログラムを書いて いた.回答のバリエーションとしては,
if(s.equals("=")) break;
の代りに,
if(s.charAt(0)=='=')break;
のようにして,文字列の比較の代りに文字列sの最初の文字を取ってから,文
字の比較をおこなうものがあった.このようにすると,if ,else if 文の代りに
switch文が使えるというメリットがある.
他のバリエーションとしては,
num=Double.parseDouble(d.readLine());
にあたるコードを
if(s.equals("+")){
// この部分
}
に入れたものなどがあった.機能を拡張したりして工夫をしたプログラムも紹
介する.
/**
* Kadai1022
*
* XXXXXXX OOOO
*
* 計算機プログラミング
* 演算子の入力と数字の入力を、Enterをはさんで交互に繰り返して計算させる。
* 数字を入力した時点で現在の計算結果を表示させる。
* 最初の入力は数字として、以降演算子(+,-,*,/)、数字の順に入力。
* 演算子として=を入力した時点で計算を終了する。
* わり算に関しては、0で除算できないようにした。
*
* 機能拡張をする際は、Operatorクラスを派生させて、新しい演算子を
* 定義する。
* そして、staticメンバのoperatorsに、演算子の文字列をキーとして、
* hashに追加する。
*
* History
* 2002.10.30 ver.0.01 新規作成
*/
import java.io.*;
import java.util.HashMap;
class Kadai1022 {
/** 演算子のリスト(Hash) */
private static HashMap operators = new HashMap();
static {
// 使用したい演算子を追加
// hashのキーに演算子の文字列を
operators.put("+", new Addition());
operators.put("-", new Subtraction());
operators.put("*", new Multiplication());
operators.put("/", new Division());
}
/**
* main関数
*/
public static void main(String[] args) throws IOException {
/** 現在計算中の数値 */
double currentNumber = 0;
// ストリームの準備
BufferedReader in
= new BufferedReader(new InputStreamReader(System.in));
// 最初の数字の代入
for (;;) {
try {
currentNumber = Double.parseDouble(in.readLine());
break;
} catch(IOException e) {
throw e;
} catch(NumberFormatException e) {
System.out.println("数字を入力してください。");
}
}
// "="を入力されるまで、演算のくりかえし
String input;
while (!(input = in.readLine()).equals("=")) {
Operator operator = ((Operator)operators.get(input));
// nullが返るのはhashにないとき
if (operator == null) {
System.out.println("その演算子はサポートしていません");
} else {
currentNumber = operator.calculate(currentNumber, in);
System.out.println("" + currentNumber);
}
}
}
}
/**
* Operatorクラス
*
* 演算子の抽象クラス。1つの数値と入力ストリームから演算結果を返す。
* MEMO:むしろインターフェースの方がいいかもしれない
*/
abstract class Operator {
abstract double calculate(double arg, BufferedReader in);
}
/**
* BiomialOperatorクラス
*
* 二項演算子の抽象クラス。
*/
abstract class BinomialOperator extends Operator {
/**
* 1回の入力で計算を行う
* 計2つの引数を使って、それぞれの具象クラスの演算結果を返します。
*/
double calculate(double arg, BufferedReader in) {
for (;;) {
/** 第2引数 */
double arg2;
for (;;) {
try {
arg2 = Double.parseDouble(in.readLine());
break;
} catch(Exception e) {
System.out.println("数値を入力してください。");
}
}
/** 計算結果 */
double answer;
try {
answer = calculate(arg, arg2);
return answer;
} catch(Exception e) {
System.out.println(e);
}
}
}
/**
* 二項演算をします。
* 具象クラスでそれぞれの演算を実装します。
* エラーはExceptionで指示。
* TODO: もっとうまいエラーの指定方法は?
*/
abstract double calculate(double arg, double arg2) throws Exception;
}
/**
* Additionクラス
* 足し算する。
*/
class Addition extends BinomialOperator {
double calculate(double arg, double arg2) throws Exception {
return arg + arg2;
}
}
/**
* Subtractionクラス
* 引き算する。
*/
class Subtraction extends BinomialOperator {
double calculate(double arg, double arg2) throws Exception {
return arg - arg2;
}
}
/**
* Multiplicationクラス
* かけ算する。
*/
class Multiplication extends BinomialOperator {
double calculate(double arg, double arg2) throws Exception {
return arg * arg2;
}
}
/**
* Divisionクラス
* わり算する。
*/
class Division extends BinomialOperator {
/**
* わり算の結果を返します。
* 0での除算はExceptionとします。
*/
double calculate(double arg, double arg2) throws Exception {
if (arg2 == 0)
throw new Exception("0では除算できません。");
return arg / arg2;
}
}
//
//
import java.io.*;
public class Kadai1022{
public static void main(String argv[]) {
BufferedReader keyin=new BufferedReader(new InputStreamReader(System.in));
double ans = 0.0;
String sign = "+";
try {
do {
double num = new Double(keyin.readLine()).doubleValue();
if (sign.equals("+"))
ans += num;
else if (sign.equals("-"))
ans -= num;
else if (sign.equals("*"))
ans *= num;
else if (sign.equals("/")) {
if (num == 0.0)
throw new ArithmeticException();
ans /= num;
}
System.out.println(ans);
sign = keyin.readLine();
}while (sign.equals("+")||sign.equals("-")||sign.equals("*")||sign.equals("/"));
} catch (IOException ie) {
System.out.println(ie);
} catch (NumberFormatException ne) {
System.out.println(ne);
System.out.println("数値型に変換できません");
} catch (ArithmeticException ae) {
System.out.println(ae);
System.out.println("0の除算エラー");
}
}
}
解答例(2)は四則演算子以外の演算子を導入したい時に,容易に追加できるよ
うに工夫したプログラムだが,一部この講義の範囲を越えている.
解答例(3)はエラー処理を徹底しておこなったプログラムである.
// 学生証番号 XXXXX
// 氏名 XXXXX
// プログラムの内容;演算子と数値を交互に入力することにより四則演算を行う。
import java.io.*;
class Kadai1022 {
public static void main(String[] args) throws IOException{
BufferedReader d = new BufferedReader(new InputStreamReader(System.in));
double sum=0.0,num;
String read;
for(;;){
read = d.readLine();
if(read.equals("=")) break;
if(read.equals("+")){
num=Double.parseDouble(d.readLine());
sum=sum+num;
}
if(read.equals("-")){
num=Double.parseDouble(d.readLine());
sum=sum-num;
}
if(read.equals("*")){
num=Double.parseDouble(d.readLine());
sum=sum*num;
}
if(read.equals("/")){
num=Double.parseDouble(d.readLine());
sum=sum/num;
}
System.out.println("sum = "+sum);
}
System.out.println("sum = "+sum);
}
}
よくあった誤答例である.電卓は一番最初に数字を入力する必要があるが,
まず演算子の入力を求めている.
//XXXXXX
//本プログラムでは、最初の入力が数字、以降、演算子、数字、演算子、数字...と入力されると想定しています。
//電卓を終了するときは=を演算子を入力する部分で入力してください。
import java.io.*;
class Kadai1022{
public static void main(String [] args) throws IOException{
BufferedReader d=new BufferedReader(new InputStreamReader(System.in));
double num1=Double.parseDouble(d.readLine());
System.out.println(num1);
for(;;){
String ope=d.readLine();
if(ope.equals("=")) break;
double num2=Double.parseDouble(d.readLine());
double ans=num1;
if(ope.equals("+")) ans=ans+num2;
else if(ope.equals("-")) ans=ans-num2;
else if(ope.equals("*")) ans=ans*num2;
else if(ope.equals("/")) ans=ans/num2;
System.out.println(ans);
}
}
}
forループの中でnum1は一度も代入されないので,ansにはnum1とnum2を最後の
演算子で計算した値になる.これは,電卓の動作及び入出力例と合わない.
MooNumber(){
int tmpDigits[]=new int[10];
int i;
// tmpDigitsに 0123456789を入れる
for(i=0;i< 10;i++){
tmpDigits[i]=i;
}
// 乱数を生成するためのクラス
Random r=new Random();
// 0 と 0-9(の範囲の乱数を添字とする tmpDigitsの要素)を,
// 1と1-9を, 2と2-9を,3と3-9を入れ換える
for(i=0;i< 4;i++){
int j=i+r.nextInt(10-i);
digits[i]=tmpDigits[j];
tmpDigits[j]=tmpDigits[i];
}
}
の,
for(i=0;i< 4;i++){
int j=i+r.nextInt(10-i);
digits[i]=tmpDigits[j];
tmpDigits[j]=tmpDigits[i];
}
の部分は,自然に書くと以下のようになる.
// tmpDigitsを乱数で並び替え
for(i=0;i< 10;i++){
int j=i+r.nextInt(10-i);
// tmpDigits[i]とtmpDigits[j]を交換
int tmpVal=tmpDigits[j];
tmpDigits[j]=tmpDigits[i];
tmpDigits[i]=tmpVal;
}
// tmpDigitsの最初4つをdigitsにコピー
for(i=0;i< 4;i++)
digits[i]=tmpDigits[i];
しかし,最初のfor文でiを0から9まで動かしているが,jは常にi以上なので,
iが4以上の時の並び替えをやっても,digitsには関係しない.するとこの2つ
のループは一緒にすることができる.
// tmpDigitsを乱数で並び替え
for(i=0;i< 4;i++){
int j=i+r.nextInt(10-i);
// tmpDigits[i]とtmpDigits[j]を交換
int tmpVal=tmpDigits[j];
tmpDigits[j]=tmpDigits[i];
tmpDigits[i]=tmpVal;
digits[i]=tmpDigits[i];
}
ここで,ループ中のtmpDigits[i]は後で参照されない領域だということがわ
かる.そこで,tmpDigits[i]への代入を省略すると,
// tmpDigitsを乱数で並び替え
for(i=0;i< 4;i++){
int j=i+r.nextInt(10-i);
// tmpDigits[i]とtmpDigits[j]を交換
digits[i]=tmpDigits[j];
tmpDigits[j]=tmpDigits[i];
}
となるわけである.