質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.04%

お釣り計算のプログラムを実装したい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,710

score 572

前提・実現したいこと

お釣り計算のプログラムを実装したいです。

設計としては以下です。

2桁の十進数が与えられます。1つ目はアイテムの購入価格(Purchase Price: PP)です。2つ目は顧客が支払った現金(Cash: CH)です。 現在、レジの中には以下の紙幣と硬貨があります:

'PENNY': .01, 
'NICKEL': .05, 
'DIME': .10,
'QUARTER': .25,
'HALF DOLLAR': .50,
'ONE': 1.00,
'TWO': 2.00,
'FIVE': 5.00,
'TEN': 10.00,
'TWENTY': 20.00,
'FIFTY': 50.00,
'ONE HUNDRED': 100.00 
このプログラムの目的は、顧客にお釣りとして返すべき紙幣/硬貨を計算することです。

入力:
標準入力から行を読み込む必要があります。各行にはセミコロンで区切られた2つの数値が含まれています。1つ目は購入価格(PP)で、2つ目は顧客が支払った現金(CH)です。

出力:
入力の各行に対して、顧客に返すお釣りを出力します。CH<PPの場合、ERRORを出力します。CH== PPの場合、ZEROを出力します。その他のケースは、顧客に返すべきお釣りの金額に相当する紙幣/硬貨を出力します。なお、回答はアルファベット順にソートする必要があります。

テスト 1
テストの入力
15.94;16.00
期待される出力
NICKEL,PENNY

テスト 2
テストの入力
17;16
期待される出力
ERROR

テスト 3
テストの入力
35;35
期待される出力
ZERO

テスト 4
テストの入力
45;50
期待される出力
FIVE

この実装を完成させる手助けをしていただけませんでしょうか?

該当のソースコード

私が途中まで実装しているコードです。
型変換やプログラムの方向性で迷いながら実装しており、アドバイスいただけたらと思います。

package douteki;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;

public class Main {
    /**
     * Iterate through each line of input.
     */
    public static void main(String[] args) throws IOException {
        InputStreamReader reader = new InputStreamReader(System.in, StandardCharsets.UTF_8);
        BufferedReader in = new BufferedReader(reader);
        String line;
        while ((line = in.readLine()) != null) {
            String[] n = line.split(";", 0);

            BigDecimal val1 = new BigDecimal(n[0]);
            BigDecimal val2 = new BigDecimal(n[1]);
            BigDecimal bigDecimalAnswer = val2.subtract(val1);

            double[] types = getMonayCount(bigDecimalAnswer, TYPES);

            System.out.println("BigDecimal : " + bigDecimalAnswer);
        }
    }

    public static final double[] TYPES = {
            100.00, 50.00, 20.00, 10.00, 5.00, 2.00, 1.00, 0.50, 0.25, 0.10, 0.05, 0.01
    };

    public double[] getMonayCount(double monay, double[] types) {
        double[] type = new double[types.length];
        for (int i = 0; i < types.length; i++) {
            while (monay >= types[i]) {
                monay -= types[i];
                type[i] = types[i];
            }
        }
        return type;
    }
}

//private String getMoneyNm() {
//    BigDecimal num;
//    switch (num){
//    case 1:
//      System.out.println("非常に不満");
//      break;
//    case 2:
//      System.out.println("少し不満");
//      break;
//    case 3:
//      System.out.println("どちらとも言えない");
//      break;
//    case 4:
//      System.out.println("少し満足");
//      break;
//    case 5:
//      System.out.println("大変満足");
//      break;
//}
//    return num;
//}

追記(2020.4.15.13時)
実行環境Java10,Tomcat9,Eclipse4.8.0,MacOS High Sierra10.13.6

実行しましたが、期待値が出力されません。実行も終わりません。(Macなのでコマンドは回答者様のおっしゃっているものとちがうかもしれませんので調査中です。)

イメージ説明
イメージ説明
イメージ説明
イメージ説明

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • k499778

    2020/04/14 02:02

    switch文を使って数字と名前を一致させたくて使おうと思ってます。それ以前の部分をまずは解決してからですね

    キャンセル

  • jimbe

    2020/04/14 02:05

    なるほど, switch 文のサンプルを待機させてあるわけですね.

    キャンセル

  • k499778

    2020/04/14 02:28

    そうです

    キャンセル

回答 2

+2

紙幣/貨幣名と金額の対応は, switch で一つずつ行うよりも予め対応した状態に出来る構造を用いたほうが簡単でしょう.
BigDecimal と他の型が混在すると扱いが面倒になりますので, BigDecimal に統一してしまっても良いかと思います.
設計文の中に PP や CH という単語が出てきますので, 該当する変数はその名前にしておくと, 設計と対応させての確認がし易くなります.

//package douteki;
package teratail.q253431;

import java.io.*;
import java.math.BigDecimal;
import java.util.*;

/** 紙幣/貨幣 */
enum Money {
  PENNY("0.01"), NICKEL("0.05"), DIME("0.10"), QUARTER("0.25"), HALF_DOLLAR("0.50"),
  ONE("1.00"), TWO("2.00"), FIVE("5.00"), TEN("10.00"), TWENTY("20.00"), FIFTY("50.00"), ONE_HUNDRED("100.00");

  private BigDecimal value;
  Money(String value) { this.value = new BigDecimal(value); }
  BigDecimal getValue() { return value; }
  public String toString() { return super.toString().replaceAll("_", " "); }

  /** 価値の高い順の Set を返す. */
  static Set<Money> descendingValues() {
    if(descValues == null) {
      descValues = new TreeSet<>(Comparator.comparing(Money::getValue).reversed()); //大きい順
      descValues.addAll(Arrays.asList(values()));
    }
    return descValues;
  }
  static private Set<Money> descValues = null;
}

public class Main {
  public static void main(String[] args) throws IOException {
    try (InputStreamReader reader = new InputStreamReader(System.in);
         BufferedReader in = new BufferedReader(reader); ) {
      for(String line;  (line = in.readLine()) != null; ) {
        String[] items = line.split(";", 0);
        BigDecimal pp = new BigDecimal(items[0]);
        BigDecimal ch = new BigDecimal(items[1]);

        BigDecimal change = ch.subtract(pp);

        if(change.signum() < 0) {
          System.out.println("ERROR");
        } else if(change.signum() == 0) {
          System.out.println("ZERO");
        } else {
          print(prepare(change));
        }
      }
    }
  }
  /** change 分の紙幣/硬貨を用意 */
  private static Map<Money,BigDecimal> prepare(BigDecimal change) {
    Map<Money,BigDecimal> result = new HashMap<>();
    for(Money m : Money.descendingValues()) {
      if(change.compareTo(m.getValue()) >= 0) {
        BigDecimal[] dr = change.divideAndRemainder(m.getValue()); //[0]=商,[1]=剰余
        result.put(m, dr[0]);
        change = dr[1];
      }
    }
    return result;
  }
  /** お釣りの紙幣/硬貨を表示 */
  private static void print(Map<Money,BigDecimal> change) {
    Set<Money> set = new TreeSet<>(Comparator.comparing(Money::toString)); //名前順
    set.addAll(change.keySet());
    int i = set.size();
    for(Money m : set) System.out.print(m+(--i>0?",":""));
    System.out.println();
  }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/04/15 15:00

    できました。ありがとうございます。
    最後までお付き合いいただきありがとうございました。

    キャンセル

  • 2020/04/15 15:05

    よかったです.
    ちなみに Eclipse のコンソールウインドウは"それっぽく動作するモノ"ですので時々妙な挙動をすることがあります. (OS等環境に因るかもしれませんが.)
    可能であれば OS の同様の機能(?)で実行されたほうが良いかもしれません.

    キャンセル

  • 2020/04/16 00:56

    ありがとうございます。それも模索してみます

    キャンセル

checkベストアンサー

+1

元のコードを全く無視しているため回答とは言えないかもしれませんが、
テスト入力は満たすはずです。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scn = new Scanner(System.in);
        while (scn.hasNextLine()) {
            String s = scn.nextLine();
            String[] n = s.split(";");
            double price = Double.valueOf(n[0]);
            double payment = Double.valueOf(n[1]);
            double change = payment - price;
            if (change < 0)
                System.out.println("ERROR");
            else if (change == 0)
                System.out.println("ZERO");
            else
                System.out.println(currency(change));
        }
    }

    public static final double[] value = {
        100.00, 50.00, 20.00, 10.00, 5.00, 2.00, 1.00, 0.50, 0.25, 0.10, 0.05, 0.01
    };
    public static final String[] name = {
        "ONE HUNDRED", "FIFTY", "TWENTY", "TEN", "FIVE", "TWO", "ONE",
        "HALF DOLLAR", "QUARTER", "DIME", "NICKEL", "PENNY"
    };

    public static String currency(double money) {
        String str = "", sep = "";
        for (int i = 0; i < name.length; i++)
            if (money >= value[i]) {
                money %= value[i];
                str += sep + name[i];
                sep = ",";
            }
        return str;
    }
}


実行結果

15.94;16.00
NICKEL,PENNY
17;16
ERROR
35;35
ZERO
45;50
FIVE
^Z


double の 0.01、0.05、0.10 は誤差を含むので正しくない出力になる可能性があります。

追記
15.49;16.00
HALF DOLLAR

やはり double による誤差が出てしまいました。

正しくは、
15.49;16.00
HALF DOLLAR,PENNY

やはり BigDecimal を使うことにしました。

package douteki;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;

public class Main {
    public static void main(String[] args) throws IOException {
        InputStreamReader reader = new InputStreamReader(System.in, StandardCharsets.UTF_8);
        BufferedReader in = new BufferedReader(reader);
        String line;
        while ((line = in.readLine()) != null) {
            String[] n = line.split(";", 0);
            BigDecimal val1 = new BigDecimal(n[0]);
            BigDecimal val2 = new BigDecimal(n[1]);
            System.out.println(getMoney(val2.subtract(val1)));
        }
    }

    public static final BigDecimal[] types = {
            new BigDecimal("100.00"), new BigDecimal("50.00"),
            new BigDecimal("20.00"),  new BigDecimal("10.00"),
            new BigDecimal("5.00"),   new BigDecimal("2.00"),
            new BigDecimal("1.00"),   new BigDecimal( "0.50"),
            new BigDecimal("0.25"),   new BigDecimal("0.10"),
            new BigDecimal("0.05"),   new BigDecimal("0.01")
    };
    public static final String[] names = {
        "ONE HUNDRED", "FIFTY", "TWENTY", "TEN", "FIVE", "TWO", "ONE",
        "HALF DOLLAR", "QUARTER", "DIME", "NICKEL", "PENNY"
    };

    public static String getMoney(BigDecimal money) {
        if (money.signum() < 0) return "ERROR";
        if (money.signum() == 0) return "ZERO";
        String type = "", sep = "";
        for (int i = 0; i < types.length; i++) {
            if (money.compareTo(types[i]) >= 0) {
                BigDecimal[] n = money.divideAndRemainder(types[i]);
                money = n[1];
                type += sep + names[i];
                sep = ",";
            }
        }
        return type;
    }
}


入力は、Scanner ではなく、BufferedReader にしました。

追記
プログラムの終了の仕方が分からないそうですが、コードを見てください。

while ((line = br.readLine()) != null) {
これは、readLine() で 1行読み込んで、それを line に入れる。
line に文字列が入ったらそれは null ではないので、ループの中に入ります。
1行読み込むことが出来なかったら line には null が入り、ループを終了します。

キーボード入力で、「入力はもう無いよ。終わりだよ」ということを
プログラムに伝えるためには、Linux では Ctrl-D、
Windows では Ctrl-Z と Enter のキーを押します。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/04/15 12:56 編集

    動作環境はMacです
    >scn.nextLine()で実施した際にも処理が終了せず、出力されませんでした。
    コードを読んでいて出力されそうなのに出力されていないから聞いています。
    なので単純にキーボード入力の問題ではないのかなと

    キャンセル

  • 2020/04/15 17:37

    私が不思議に思うのは、Java を学習し始めて、一度もコンソール(標準入出力)を使ったことがないということです。そんなことはあり得ないと思います。いったいどうな風に学習しているのか非常に興味があります。この課題以前にどんな課題をやったのでしょうか?

    キャンセル

  • 2020/04/16 01:00

    そうですね。標準出力という言葉を理解できていなかったです。この動きに関しては実施したことがあります。
    参考書で体系的に学んだことはありますが、基本的に実務で経験しながら学んでいることが多く、実務であまりコンソールに入力してそれに対して答えが出るようなシステムは作ってきませんでした。
    標準出力という言葉とやることが紐付いていなかったです。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.04%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る