C# exercises (5) Calculator

my電卓を作ろう

逆ポーランド記法電卓

Reverse Polish Notation Calculator

画面をデザインする

スクリーンショット 2016-05-02 16.20.21

ツールボックスの中で、

  • コモンコントロール「TextBox」
    • TextBox –>  (name): typeText;  text: 0
    • TextBox –> (name): lastText;  text: 0
  • コモンコントロール「Button」
    • Button1 –> (name): plusButton;  text: +
    • Button2 –> (name): subButton; text: –
    • Button3 –> (name): multiButton; text: x
    • Button4 –> (name): divButton; text: /
    • Button5 –> (name): clrButton; text: CL
    • Button6 –> (name): enterButton; text: ER

Formにドラッグ&ドロップする

プログラミング

KeyPressイベントを作成

Form1を選択し、Form1のイベントの表示に切り替えて、「KeyPress」イベントに、FormKeyPressを入力

スクリーンショット 2016-05-02 16.23.53

FormKeyPressメソッドを作成

六つボタンを選択した状態で、コントロール類の、「KeyPress」イベントに、FormKeyPressimageを選択。

スクリーンショット 2016-05-02 16.28.36

private void FormKeyPress(object sender, KeyPressEventArgs e)
{
    string key = e.KeyChar.ToString();
    int n = 0;
    try
    {
        n = int.Parse(key);
    }
    catch
    {
        return;
    }
    string num = typeText.Text + n;
    try
    {
        typeText.Text = "" + int.Parse(num);
    }
    catch
    {
        return;
    }
}

plusButtonClickメソッドを作成

スクリーンショット 2016-05-02 16.41.55

private void plusButtonClick(object sender, EventArgs e)
{
    try
    {
        int lastnum = int.Parse(lastText.Text);
        int typenum = int.Parse(typeText.Text);
        lastText.Text = "" + (lastnum + typenum);
        typeText.Text = "0";
    }
    catch
    {
        return;
    }
}

subButtonClickメソッドを作成

スクリーンショット 2016-05-02 16.42.20

private void subButtonClick(object sender, EventArgs e)
{
   try
   {
       int lastnum = int.Parse(lastText.Text);
       int typenum = int.Parse(typeText.Text);
       lastText.Text = "" + (lastnum - typenum);
       typeText.Text = "0";
   }
   catch
   {
       return;
   }
}

multiButtonClickメソッドを作成

スクリーンショット 2016-05-02 16.43.30

private void multiButtonClick(object sender, EventArgs e)
{
    try
    {
        int lastnum = int.Parse(lastText.Text);
        int typenum = int.Parse(typeText.Text);
        lastText.Text = "" + (lastnum * typenum);
        typeText.Text = "0";
    }
    catch
    {
        return;
    }
}

divButtonClickメソッドを作成

スクリーンショット 2016-05-02 16.44.14

private void divButtonClick(object sender, EventArgs e)
{
    try
    {
        int lastnum = int.Parse(lastText.Text);
        int typenum = int.Parse(typeText.Text);
        lastText.Text = "" + (lastnum / typenum);
        typeText.Text = "0";
    }
    catch
    {
        return;
    }
}

clrButtonClickメソッドを作成

スクリーンショット 2016-05-02 16.45.07

private void clrButtonClick(object sender, EventArgs e)
{
    if (typeText.Text == "0")
    {
        lastText.Text = "0";
    }
    typeText.Text = "0";
}

enterButtonClickメソッドを作成

スクリーンショット 2016-05-02 16.47.49

private void enterButtonClick(object sender, EventArgs e)
{
    string s = typeText.Text;
    lastText.Text = s;
    typeText.Text = "0";
}

作成したコード

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Calculator
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void FormKeyPress(object sender, KeyPressEventArgs e)
        {
            string key = e.KeyChar.ToString();
            int n = 0;
            try
            {
                n = int.Parse(key);
            }
            catch
            {
                return;
            }

            string num = typeText.Text + n;
            try
            {
                typeText.Text = "" + int.Parse(num);
            }
            catch
            {
                return;
            }

        }

        private void plusButtonClick(object sender, EventArgs e)
        {
            try
            {
                int lastnum = int.Parse(lastText.Text);
                int typenum = int.Parse(typeText.Text);
                lastText.Text = "" + (lastnum + typenum);
                typeText.Text = "0";
            }
            catch
            {
                return;
            }
        }

        private void subButtonClick(object sender, EventArgs e)
        {
            try
            {
                int lastnum = int.Parse(lastText.Text);
                int typenum = int.Parse(typeText.Text);
                lastText.Text = "" + (lastnum - typenum);
                typeText.Text = "0";
            }
            catch
            {
                return;
            }

        }

        private void multiButtonClick(object sender, EventArgs e)
        {
            try
            {
                int lastnum = int.Parse(lastText.Text);
                int typenum = int.Parse(typeText.Text);
                lastText.Text = "" + (lastnum * typenum);
                typeText.Text = "0";
            }
            catch
            {
                return;
            }

        }

        private void divButtonClick(object sender, EventArgs e)
        {
            try
            {
                int lastnum = int.Parse(lastText.Text);
                int typenum = int.Parse(typeText.Text);
                lastText.Text = "" + (lastnum / typenum);
                typeText.Text = "0";
            }
            catch
            {
                return;
            }

        }

        private void clrButtonClick(object sender, EventArgs e)
        {
            if (typeText.Text == "0")
            {
                lastText.Text = "0";
            }
            typeText.Text = "0";
        }

        private void enterButtonClick(object sender, EventArgs e)
        {
            string s = typeText.Text;
            lastText.Text = s;
            typeText.Text = "0";
        }
    }
}

My電卓使い方

計算例: 123+456=

まずキーボードから数字123入力

スクリーンショット 2016-05-02 17.01.50

「ER」キーで数字を設定

スクリーンショット 2016-05-02 17.01.53

次の数字456を入力

スクリーンショット 2016-05-02 17.02.00

演算キーで計算

スクリーンショット 2016-05-02 17.02.11

Calculator

電卓プログラムの考え方、書き方例

2つの数値の加減乗除をする電卓のようなプログラムを作ります。
簡単なようですが、多くの話題(データ型の選択、入力、判断や繰り返しなど)を含んでおり、学習に役立つ例題です。
今回は、プログラムをつくる流れも分かるように、どんなプログラムにするか検討するところから始めます。 それを処理手順に書き、C言語のコードに直します。

プログラムの機能を考える

まずは、どんなプログラムにするかを考え、前提とすることや制約についても検討します。

  1. 数や演算の指定はどうする?
    たとえば「2 + 5」のように、「数値1 演算記号 数値2」の順に入力すると計算結果が表示されるようにする。
    演算記号は、+, -, *, / のみとする。
  2. 繰り返して計算できるようにする
    繰り返しの終了は、指定が上記の書式でなかったときとしよう。
  3. 他に考えておくことは?
    (1) 浮動小数点数でも計算できるようにする
    (2) 加減乗除以外の演算記号が指定されたらどうする?
    (3) 割り算で、ゼロ割しようとしたときはどうする?
    (4) 電源ON の代わりに指定の書式を示すメッセージを表示しよう。電源OFF のメッセージも表示しよう
    (2) と (3) のケースは、エラー・メッセージを表示して、指定し直せるようにしよう。

処理手順として書く

上の検討結果を踏まえて、処理内容を具体的かつ端的に書きます。 判断や繰り返し、データの入出力も分かるようにします。
下記では、チャートを使わずに書きます。

電卓プログラムの処理手順 】
指定の書式を示すメッセージを表示する
<繰り返し>
|  「数値1 演算記号 数値2」の指定を受け取る –> 変数 a, op, b へ
|    指定の書式でなかったとき) 繰り返しを抜ける
|  演算記号 op に応じた計算をする –> ans へ結果を入れる
|    + のとき) a + b を求める
|    - のとき) a – b を求める
|    * のとき) a * b を求める
|    / のとき) b がゼロでないかチェックする
|          ゼロのとき) エラー・メッセージを表示して繰り返しの先頭に戻る
|          a / b を求める
|    それ以外) エラー・メッセージを表示して繰り返しの先頭に戻る
-  答え ans を表示する
電源OFF のメッセージを表示する

プログラムコードに直す

上記の手順を素直にC言語で書きます。

電卓プログラムの書き方例 】

#include <stdio.h>
main()
{
    double  a, b, ans;
    char    op;

    printf( "加減乗除(+,-,*,/)ができます。指定例:2+5、終了時はq\n" );
    while( 1 ) {
        printf( "ready : " );
        if( scanf( "%lf %c %lf", &a, &op, &b ) != 3 ) break;
        switch( op ) {
        case '+': ans = a + b; break;
        case '-': ans = a - b; break;
        case '*': ans = a * b; break;
        case '/': if( b == 0.0 ) {
                     printf( "Error!(ゼロでの割算はできません)\n" );
                     continue;
                  }
                  ans = a / b; break;
        default:  printf( "Error!(演算記号の指定が誤りです)\n" );
                  continue;
        }
        printf( "--> %g\n", ans );
    }
    printf( ".... Power OFF\n" );
}

 実行例

加減乗除(+,-,*,/)ができます。指定例:2+5、終了時はq
ready : 6.5 * 3
--> 19.5
ready : 7 % 4
Error!(演算記号の指定が誤りです)
ready : 123 / 2
--> 61.5
ready : 7 + 16
--> 23
ready : q
.... Power OFF

 

Reverse Polish Notation Calculator

逆ポーランド記法

逆ポーランド記法

逆ポーランド記法を使えば、式の計算をする(評価)には、先頭からひとつずつ順番に記号を読み込み、その記号が演算子以外であればスタックに値を積み、演算子であればスタックから値を取り出して演算し結果をスタックに積む、という簡単な操作の繰り返しだけでよい。そのため、プログラミング初心者の練習課題として、逆ポーランド記法の電卓を作ることがよく行われる。

逆ポーランド記法による計算の例

2+3を計算するとき,逆ポーランド記法では,次のように表す.数値や演算子(+, -, *, /)の間にはスペースを設ける.

2 3 +

これはいくつかのメモリー(記憶場所)が準備されているとき,

  • 2を1番目のメモリーに記憶
  • 3を2番目のメモリーに記憶
  • 1番目のメモリーの内容と2番目のメモリーの内容を加算
  • 加算結果を1番目のメモリーに記憶

という手順で計算することを表している.

特徴:

  1. 日本語の並びと同じ計算順序
  2. 逆ポーランドには括弧がない
  3. キータッチ数は最少

o0125007411785329651

【中置記法】
3 * ( 1 + 2 ) / ( ( 4 – 5 ) / ( 6 + 7 ) ) =  キータッチ数は22回

【逆ポーランド記法】
3 1 2 + * 4 5 – 6 7 + / /           キータッチ数は13回

問題

次のような機能を持つ電卓プログラムを実現せよ。

    1. 基本機能
      1. 演算子は+,-,*,/を許す。
      2. 被演算子にはdouble型を許す。
      3. 式の記法には逆ポーランド記法を用いよ。
      4. 演算子=により結果を表示する。
    2. 数学関数
      1. sin,cos,tan,exp,sqrなど

文字列解析、配列によるスタック実装など、総合的な演習問題としてよく使われます。K&Rにも同様の演習があります。

回答例

#define STACK_DEPTH 100
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<math.h>

double stack[STACK_DEPTH];
int sp=STACK_DEPTH;

double pop(void){ return (sp<STACK_DEPTH)?stack[sp++]:0.0; }
void push(double f){ if(sp>0)stack[--sp]=f; }
int getword(char* dst, const char* str, int limit) {
    int i, j, len = strlen(str);
    for(i=0; i<len && isspace(str[i]); i++);
    for(j=0; j<limit && (j+i)<len && !isspace(str[i+j]); j++) dst[j]=str[i+j];
    dst[j]='\0';
    return i+j;
}

int main(void)
{
    char line[100], tmp[100];
    int i, c;
    while(1){
        i = 0;
        fgets(line, 100, stdin);
        while((c=getword(tmp, &line[i], 100)) && strlen(tmp)){
            if(strcmp(tmp, "sin")==0) push(sin(pop())); else
            if(strcmp(tmp, "cos")==0) push(cos(pop())); else
            if(strcmp(tmp, "sqr")==0) push(pow(pop(),2)); else
            if(strcmp(tmp, "+")==0) push(pop()+pop()); else
            if(strcmp(tmp, "-")==0) push(pop()-pop()); else
            if(strcmp(tmp, "*")==0) push(pop()*pop()); else
            if(strcmp(tmp, "/")==0) push(pop()/pop()); else
            if(strcmp(tmp, "=")==0) printf("%f\n", pop()); else
            push(atof(tmp));
            i+=c;
        }
        if (line[0] == '\n') break;
    }
    return 0;
}