通貨は何枚必要?(java)

かなり滞っていたJavaの勉強を再開しました。。

3年間のプログラミングから離れていたため、基礎の基礎の基礎で、とりあえず、いろいろな問題を解く作業をしています。

ただ、難易度の低い「練習課題」は意外に少なくって、とりあえず例題を探して、ひたすらJavaで解くという作業をしています。

<Java 練習問題>

http://kitako.tokyo/lib/JavaExercise.aspx?id=107

<C言語 練習問題>

http://www.geocities.jp/kenji_y0328/crenshu/

その中で、「これをオブジェクト指向で解くなら、どう設計すればよいのか、、」と考えた問題があったので、メモ。

いわゆる、上から下へ処理が流れていく手続き型言語でしか設計、コーディングしたことがないので、このオブジェクト指向になかなかなじめず、頭を悩ませています、、が、とりあえず考えてみました。

一応、現段階での考えを、文章に残し、後程その考えてが誤ってなかったか検証しようと思います。

【問題】

入力された金額に対して、必要な紙幣、硬貨の枚数を表示する。

例えば、「1,115円」が入力されたとしたら

1000円→1枚

500円→0枚

100円→1枚

10円 →1枚

5円  →1枚

1円  →0枚

が必要となりますが、これをプログラムで自動的に算出しなさい、という問題です。

【問題を理解する】

さて、まずは、アルゴリズムを考えます。

入力された金額を「金額」と表記します。

前提として、必要な紙幣、硬貨の「必要最低限の枚数」で表すこととします。要は、「1000円」を「金額」としたときには、「500円が2枚」ではなく「1000円が1枚」と表記します。

なので、「より大きな通貨から、必要な枚数を決定してく」ことが必要です。

これを数式にて表現すると

「1000円」の「枚数」 = 「金額」/ 1000円

となります。

このあと、次に大きな単位である「500円」の硬貨の必要枚数を算出しますが、1000円で割った「余り」が残りの金額になりますね。(例えば「金額」が2500円としたら、「金額」/「1000円」の、余りは、500円です。)

なので、金額を更新します。

「金額」 = 「金額」 % 「1000円」

なお、プログラミングの際の「=」(等号)は、論理式ではなく「代入する」意味で使われるので、注意してください。

また、「%」は、除算したあとの「あまり」を計算してくれます。

上の式は、「金額」という変数に、「金額」を「1000円」で割った余りを代入するという意味です。

この金額をさらに、「500円」で割った答えが、「500円」の枚数となります。

これを、すべての硬貨分「繰り返す」と、各硬貨における必要枚数が計算できそうです。

ただし、「金額」が「0」になった時点で、残りの計算はする必要はなさそうです。

【データ構造を考える】

「金額」は、「円」ですから、整数値ですね。今回は、莫大な金額は取り扱わないこととし、整数型Integerで宣言することにします。

さて、ここで「オブジェクト指向」をまったく意識しないとすれば、いわゆる手続き型で書いてしまえば一瞬でかけてしまいます。

それでは面白くないので、「通貨」という概念を、オブジェクト指向的な「クラス」として表現しようと思います。

class JapaneseCurrency{

//フィールドに貨幣の種類を持たせる
private final Integer currency[] = {1,5,10,100,500,1000,5000,10000};

//貨幣の種類「配列」を返す
public Integer[] getCurrency(){
return currency;
}

}

と定義してみました。

Javaなどのオブジェクト指向言語のクラスとは、C言語における構造体が、メソッド(機能)を持ったと考えれば、わかりやすいと思います。

今回は、日本の通貨をクラス:JapanCurrencyとして定義してみました。

JapanCurrencyは、フィールドとして、硬貨、紙幣の種類をInteger型の配列を持っています。(フィールドは、クラス内部の変数を指し示す用語です。)

privateは、「アクセス修飾子」と呼ばれるもので、その変数がどのクラスからアクセスできるかを指し示します。privateは、そのクラスしかアクセスすることはできません。

finalは、ファイナル修飾子と呼ばれるもので、finalをフィールドに適用することで、これ以降、この変数に変更を加えることはできません。

→つまり、privateとfinalを付与するとそのクラス内での「定数」として扱えるということですね。

Integerですが、int型とは少し違います。これは「ラップクラス」とも呼ばれますが、その名の通り「クラス」です。プリミティブ型のIntを宣言したときとは違います。

int型では、他言語のint型と同様、int型に必要数のメモリ領域を、用意するだけですが、Integerは「クラス」であり、int型を操作するメソッドを含めてラッピングしたObject型となっています。

また、通貨を取得するための、メソッドとして、getCurrencyというメソッドを用意しました。

Javaでは、getter、setter(ゲッター、セッター)といって、メソッドを介してフィールドを操作することが一般的だそうです。こうやってクラスのフィールドを隠蔽し、外部から操作できなくすることを「カプセル化」といいます。

今回はsetterは用意しませんでしたが、getCurrencyというgetterを使用して、Integer型の配列を取得することとしました。

今回は、貨幣とそれに対応する枚数を紐づけるためにHashMapというクラスを使用しました。ハッシュマップとは、「連想配列」という言葉にするとわかりやすいでしょう。

HashMapは、ユーティリティクラスとして、用意されています。

配列のように引数で要素を指定するわけではなく「1000」というワードと「2枚」というワードを結びつけることで、直感的な操作を可能とします。ただ、ハッシュマップは、順番が保証されていません。

今回は、順番に表示したかったので、Arrayクラスの静的メソッドを使用して、ソートしました。あまり、うまい実装とはいえませんね。。

以下にソースを示します。

今回は、データ構造として、ハッシュマップを使用したことが、ソートなど余計な実装を生んでしまいました。とにかく、いろいろ作りながら、覚えていきたいと思います。

import java.util.*;

public class MyApp{

public static void main(String[] args){

Integer currency;
int money = 56990;
int count;

//通貨を数えるハッシュマップのcounter(カウンター)をインスタンス化
Map<Integer,Integer> counter = new HashMap<>(); //貨幣、枚数

JapaneseCurrency jpcr = new JapaneseCurrency();
Integer currencys[] = jpcr.getCurrency();

//Comparatorも静的メソッドのため使用可能
Arrays.sort(currencys , Comparator.reverseOrder());

for (int c : currencys){
System.out.println(c);
}

for(int c : currencys){

if (money >= c){
count = money / c;
System.out.println(c + "円の枚数は:" + count );
money = money % c;
counter.put(c,count);
}else{
count = 0;
System.out.println(c + "円の枚数は:" + count );
counter.put(c,count);
}

}

for(Map.Entry<Integer,Integer> c : counter.entrySet()){
System.out.println(c.getKey() + ":" + c.getValue());
}

}
}

実行結果

最後にハッシュマップを表示した際は、順番が保証されていなので、結果のとおり、ばらばらに表示されています。。いかに有効なデータ構造を選択するかは経験と、デザインパターンを数多く知っておく必要がありそうです。

10000円の枚数は:5
5000円の枚数は:1
1000円の枚数は:1
500円の枚数は:1
100円の枚数は:4
10円の枚数は:9
5円の枚数は:0
1円の枚数は:0
10000:5
1:0
500:1
100:4
5:0
5000:1
1000:1
10:9

この記事が気に入ったらサポートをしてみませんか?