漢数電卓という選択肢

#犯行動機
JavaFXを始めるとだいたい電卓に行き着くじゃないですか

チュートリアル通り普通の電卓やっても面白くないし、

- むしゃくしゃしてやった
- 反省はしている


#イメージ

- ボタンを増やすと画面設計がそもそも大変
- 入力が漢数字はやりにくい

ということから、普通の電卓と基本は同じになるでしょう

[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/f67782bc-6e4f-79bb-4bf4-e3b1b877ad07.png)](https://www.irasutoya.com/2013/03/blog-post_3975.html)
[電卓のイラスト（文房具）](https://www.irasutoya.com/2013/03/blog-post_3975.html)

そこで気付いた
漢数字って**縦書きじゃね？**

**つまり、表示画面は縦書きにする必要がある**

#画面レイアウト
つまりこういうことだな
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/bc4d1f93-bdea-1e83-0f19-6e7249083d50.png)
~~これはひどい~~
素晴らしいデザインになりました
なお、labelに縦書き機能はないし、文字数が入りきらないから改行する機能を利用して、横幅1文字分にすることで縦書きにする

~~フォントを変えればよかったのでは~~










#作りましょう
犯行に至ります
ちなみに、普段がC#なので、クセでメソッド名が大文字から始まってます
見た目はこっちの方が好きなんだ ごめんねJava

##変数
四則演算用のモードとその変数を準備
計算に使う文字列と表示する時に使う文字列を用意

```.java
enum Calc{Plus,Minus,Multiplied,Dividid}
    
@FXML
Label label;
    
Calc calcMode = Calc.Plus; //四則演算モード管理変数
String calc = "",Output = ""; //計算用文字列と表示用文字列
```

ここら辺をこねくり回してしまえば計算できるでしょう

##メソッド
###ボタン
ボタンが押された時の処理をメソッドに集結させて楽します

```.java
    @FXML void Num0(){ InputNum(0); }
    @FXML void Num1(){ InputNum(1); }
    @FXML void Num2(){ InputNum(2); }
    @FXML void Num3(){ InputNum(3); }
    @FXML void Num4(){ InputNum(4); }
    @FXML void Num5(){ InputNum(5); }
    @FXML void Num6(){ InputNum(6); }
    @FXML void Num7(){ InputNum(7); }
    @FXML void Num8(){ InputNum(8); }
    @FXML void Num9(){ InputNum(9); }
    @FXML void Plus(){calcMode = Calc.Plus; InputNum(); }
    @FXML void Minus(){calcMode = Calc.Minus; InputNum(); }
    @FXML void Multiplied(){calcMode = Calc.Multiplied; InputNum(); }
    @FXML void Divided(){calcMode = Calc.Dividid; InputNum(); }
    @FXML void Clear(){label.setText(calc = Output = "");}
    @FXML
    void Equal(){
        String[] s = calc.split(" ");
        int a = Integer.parseInt(s[0]);
        int b = Integer.parseInt(s[1]);
        int ans = 0;
        switch(calcMode){
            case Plus:
                ans = a + b;break;
            case Minus:
                ans = a - b;break;
            case Multiplied:
                ans = a * b;break;
            case Dividid:
                ans = a / b;break;
        }
        System.out.println(a + "" +  calcMode + b + " = " + ans);
        //結果が0なら零、負なら「負 結果」になるように三項演算子
        Output = Output + "\n⇓\n" 
                + (ans != 0 ?
                            (ans > 0 ? Japaneser(ans):"負" + Japaneser(-ans))
                            : "零");
        
        label.setText(Output);
    }
```

Equalが押されたら演算モード次第で計算し表示、
数字と四則演算の処理は以下のメソッドへ

```.java
    //数字を文字列にして渡す
    void InputNum(int num){
        InputNum(Integer.toString(num));
    }
    
    //四則演算は空白として文字列に追加
    void InputNum(){
        InputNum(" ");
    }

    //計算用文字列に引数を追加
    void InputNum(String str){
        calc = calc + str;
        System.out.println("*デバッグ calc=>" + calc + ":");
        Output();
    }
```

###表示
空白がないと、四則演算を行わないので、計算用文字列を漢数字にそのまま変換します

```.java
    //ラベル更新メソッド
    void Output(){
        if(!calc.contains(" ")){ //空白がなければそのまま
            Output = Japaneser(calc);
        }
        else{
            String[] calcStr = calc.split(" ");//空白で区切る
            Output = Japaneser(calcStr[0])+ Calc() + 
                   (calcStr.length > 1? Japaneser(calcStr[1]) : "");
        }
        label.setText(Output);
    }
    //四則演算文字メソッド
    String Calc(){
        switch(calcMode){
            case Plus:return "\n足\n";
            case Minus:return "\n引\n";
            case Multiplied:return "\n掛\n";
            case Dividid:return "\n割\n";
            default :return null;
        }
    }
```

###漢数字にしよう
算用数字を漢数字に変換するには
10,100,1000,10000で区切りを付けて漢数字にする必要がある
となると、算用数字の桁数を知ればいけるかな

```.java
    //数字を漢数字に翻訳
    String Japaneser(int num){
        String str = "";
        int digit = (int)Math.pow(10,(num+"").length()-1);//文字数から桁数を出す
        
        for(; num != 0; digit /= 10){
            if(num >= digit){
                int divid = num/digit;
                str = str
                        +(num == 1 || divid != 1 ? ToJp(divid) : "")
                        +(digit != 1 ? ToJp(digit) : "");//0対策
                num -= divid * digit; //上の桁から漢数字に変換していく
            }
        }
        return str;
    }
    
    String Japaneser(String nums){
        if(nums.length() > 4) return label.getText();
        return Japaneser(Integer.parseInt(nums));
    }
    
    //数字を漢数字に変換
    String ToJp(int num){
        switch(num){
            case 1:return "一";
            case 2:return "二";
            case 3:return "三";
            case 4:return "四";
            case 5:return "五";
            case 6:return "六";
            case 7:return "七";
            case 8:return "八";
            case 9:return "九";
            case 10:return"十";
            case 100:return"百";
            case 1000:return"千";
            default: 
                System.out.println("!!万の位には対応していません!!");
                return null;
        }
    }
```

なお、万の位を超えると大変だったので、未対応です
そこに関して電卓としてのプライドは捨ててます

#仕様通りに使用しよう

##足し算
`9+46=55`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/753ce8ca-07e1-7b21-7635-31f5192ac3ff.png)



`999+3=1002`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/ad52908e-57aa-7e54-e9ab-1badc98da024.png)









##引き算
`79-28=51`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/12f4f8cb-575b-ea51-53dc-80e09b5f9e34.png)


`58-198=-140`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/e15a9158-ede2-9344-94b6-9d6d027c5b04.png)
きちんと負の数にも対応できてますね




















##掛け算
`53*6=318`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/edd16636-ed95-89d1-7a2f-3ab51feee050.png)







##割り算
`150/3=50`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/b0ed1690-9cb7-8c1b-09f7-fb00e852be8b.png)



`100/3=33`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/75d8bc9a-e387-8ec9-89be-5fe763986edf.png)

そういえばintでやってるから小数ねぇ！！

まぁいっか！

#評価
制作中の画像を見せたところ、`「結果」じゃなくて「↓」なのか`というお声をいただき、やってみたところ↓


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/67965029-f0f6-ca1c-86a7-36150b491811.png)


**こ　れ　は　読　め　ね　ぇ**


- 「マイナスを負としてるのは面白い」
- 「レ点が入ってませんよ」
- 「白文だね」
- 「予想の斜め上だった」

などの~~微妙な反応~~素晴らしいお声をいただき、~~悪夢を続ける！~~続編を作成！



#画面レイアウト ver2
続編では漢数電卓がパワーアップして帰ってきた！！
漢数字にちなみ「和」の表現を増すために木目調の画像を使用し
配色も変更することで、より使いやすさを増しました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/e98ee99f-7d15-d23f-26ad-a3078035ba38.png)


前作に比べ、

- 一般的な電卓の表示に変更（「一足二→三」のような式の表示を廃止）
- 負数にできる（なぜなかった）
- √（根）
- 小数（やっとか）

を実装しました




#プログラミングる

##変数
変数名は違いますが役割は前作と似たようなものです

```.java
    private String op = null;
    private double result = 0;
    private double value = 0;
    // 表示欄のラベル
    @FXML private Label label;
    String calc = "";
```

##メソッド
###数字ボタン

```.java
    // 数字ボタンが押されたときの処理
    @FXML private void numberPressed(ActionEvent event) {
        Button bt = (Button) event.getSource();// 押されたボタンとその文字を取得する
        String digit = ToMath(bt.getText()); //漢数字を算用数字に変換
        calc = calc + digit;
        label.setText(Japaneser(calc));
        value = Double.parseDouble(calc);
    }
```

###演算子ボタン

```.java
    // 演算子ボタンが押されたときの共通処理
    @FXML private void operatorPressed(ActionEvent event) {
        Button bt = (Button) event.getSource();// 押されたボタンとその文字を取得する
        String opname = bt.getText();

        if (null == op) {
            if(!opname.equals("根"))result = value;
            else result = Math.sqrt(Double.parseDouble(calc));
        } else { 
            switch (op) {
                case "足":
                    result = result + value;
                break;
                case "引":
                    result = result - value;
                break;
                case "掛":
                    result = result * value;
                break;
                case "割":
                    result = result / value;
                break;
                case "根": //平方根を連打できるように
                    result = Math.sqrt(result);
                    break;
                default:
                break;
            }
        }
        label.setText((result >= 0 ? //正負で処理を分ける
                        Japaneser(Double.toString(result)):
                        "負" + Japaneser(Double.toString(-result)))); 
        calc = "";
        
        if (opname.equals("結果")) {
            op = null;
        } else {
            op = opname;
        }
        value = 0;
    }
```

###小数ボタン

```.java
    // 小数点ボタンが押された時の処理
    @FXML private void pointPressed() {
        String str = label.getText();
        if(str.contains("・")){
            String[] calcs = calc.split("\\.");
            calc = calcs[0] + (calcs.length>1 ? calcs[1] : "");
            str = Japaneser(calc);
        }
        else{
            str = str + "・";
            calc = calc + ".";
        }
        
        label.setText(str);
    }
```

###クリアボタン

```.java
    // C（クリア）ボタンが押された時の処理
    @FXML private void clearPressed() {
        op = null;
        result = value = 0;
        calc = "";
        label.setText("");
    }
```

###ルートボタン

```.java
    @FXML void Root(){
        double r = Math.sqrt( Double.parseDouble(calc));
        result = r;
        calc = Double.toString(r);
        label.setText(Japaneser(calc));
    }
```

###正負ボタン

```.java
    @FXML void Minus(){
        String str = label.getText();
        if(str.contains("負")){
            str = str.substring(1);
        }
        else{
            str = "負" + str;
        }
        value *= -1;
        calc = "-" + calc;
        label.setText(str);
    }
```

###漢数字変換

```.java
    //数字を漢数字に翻訳
    String Japaneser(int num){
        String str = "";
        int digit = (int)Math.pow(10,(num+"").length()-1);
        
        for(; num != 0; digit /= 10){
            if(num >= digit){
                int divid = num/digit;
                str = str
                    +(num == 1 || divid != 1 ? ToJp(divid) : "")//一残り対策
                    +(digit != 1 ? ToJp(digit) : "");//桁上がり0対策
                num -= divid * digit; //上の桁から漢数字に変換していく
            }
        }
        return str;
    }
    
    //文字列（数字）を漢数字に変換
    String Japaneser(String str){
        String[] strs = str.split("\\.");
        String round = (strs[0].equals("0") ? //0だったら零
                "零" :
                Japaneser(Integer.parseInt( strs[0])));//整数部分を翻訳
        if(strs.length > 1){
            String[] chars = strs[1].split(""); //1文字づつに分解
            if(chars.length > 1 || !chars[0].equals("0")){
                round = round + "・";
                for(String s : chars){
                    round = round + ToJp(Integer.parseInt(s));
                }
            }
        }
        return round;
    }
    
    //漢数字を数字に変換
    String ToMath(String jp){
        switch(jp){
            case "桁増":
            case "零":return "0";
            case "一":return "1";
            case "二":return "2";
            case "三":return "3";
            case "四":return "4";
            case "五":return "5";
            case "六":return "6";
            case "七":return "7";
            case "八":return "8";
            case "九":return "9";
            case "・":return ".";
            case "負":return "-";
            default: return "E";
        }
    }
    
    //数字を漢数字に変換
    String ToJp(int num){
        switch(num){
            case 0:return "零";
            case 1:return "一";
            case 2:return "二";
            case 3:return "三";
            case 4:return "四";
            case 5:return "五";
            case 6:return "六";
            case 7:return "七";
            case 8:return "八";
            case 9:return "九";
            case 10:return"十";
            case 100:return"百";
            case 1000:return"千";
            case 10000:return"万";
            default: 
                System.out.println("Error");
                return null;
        }
    }
```

#使ってみよう
前作と違い、一般的な電卓と同じく連続で計算できます
`1+2+3 / 5 * 6`みたいな
##引き算
`65-96=-31`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/ed3b3865-55db-99f8-432b-5e5fb460ccd0.png)

この状態で`正負`を押すと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/e953896a-4a5a-bb8c-0098-80de19ff67be.png)





##割り算
`1096/3=365.3333333333333`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/174d6cfd-fe07-b5c4-b4ab-f5772aa53543.png)


##ルート
`√5 = 2.23606797749979`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554244/ddf0c57f-6e05-7390-ab74-f4cbb6474e5b.png)



#まとめ
いかがだったでしょうか ~~万の桁にも対応できてませんが~~
このデザイン性と使い勝手はその方面からは「これは凄い」と評価頂けるのではないでしょうか

- 入力は算用数字なのに表示は漢数字
- そもそも使いにくい
- 見にくい

など、様々な感想を ~~持ったので~~ お持ちと思いますので、お読みの皆様も漢数電卓をデザインしてみると面白いと思います

#漢数電卓という選択肢
以上のように漢数電卓には様々な可能性を秘めています
そのため、今後 電卓を使う際の選択肢に漢数電卓は、**`なし`**です
当たり前ですね