解答例(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]; }となるわけである.