こんばんは。まるです。
もうかれこれ3年近く前に電卓の記事を書いたのですが、あの頃より簡単にコーディングできる方法を覚えたので備忘録的に紹介しようと思います。
今回紹介する電卓は、文字列の式を計算できます!
どういうことかというと、
string strKeisanShiki = " 1+2+3 " ;
のような文字列である式そのものを計算することができます。
また、
string strKeisanShiki = "(-1+2)*(4/2) " ;
のような「()」や負の数の計算まで実現できます。
しかもほぼ50行で書けます!
3年前に書いた電卓は、例えば「1 + 2 + 3」を行おうと思った時は、
「1 」→「+」→「2」→「+」→「3」→「= 」
のように順番にボタンを押下していき、
最初の「1 + 2 」の次に押された四則演算ボタンのイベントで一度計算結果を変数に格納してその先の「+ 3 」を行うというものでした。
一番オーソドックスで普通に考えればそのように書くのと思いますが、
これだとコードが非常に長くなってしまいます。
3年前に記述したコードが全体で600行弱となっており結構なボリュームです。
※コメントや四則演算以外の計算(例えば√とか)も含む
今回紹介するコードは細かい機能やコメント、四則演算以外のものは割愛していますが、括弧を使った式などを実装して約50行で済みます。
下のデザインに配置してあるボタンすべての計算を実装しても100行程で実装可能です。
前置きが長くなりましたが解説に入ります。
まずデザインです。
数字のボタンや小数点、四則演算と括弧はすべて同じイベント参照してます。
次に以下がコードです。
using System;
using System.Data;
using System.Text;
using System.Windows.Forms;
namespace MainForm
{
public partial class MainForm : Form
{
StringBuilder sb = new StringBuilder();
DataTable dt = new DataTable();
public MainForm()
{
InitializeComponent();
}
private void btn_clicked(object sender, EventArgs e)
{
sb.Append(((Button)sender).Text);
txtCbox.Text = sb.ToString();
}
private void btnEqual_Click(object sender, EventArgs e)
{
try
{
rtbDisplay.Text = Convert.ToDecimal(dt.Compute(sb.ToString(), "")).ToString();
}
catch
{
rtbDisplay.Text = "不正な式です。";
}
}
private void btnClear_Click(object sender, EventArgs e)
{
sb.Clear();
rtbDisplay.Clear();
txtCbox.Clear();
}
private void btnBackSpace_Click(object sender, EventArgs e)
{
if (sb.Length > 0)
{
sb.Remove(sb.Length - 1, 1);
txtCbox.Text = sb.ToString();
}
}
}
}
空白行を含めて全部で52行で記述出来てます!
※私はコーディングガチ勢ではないのでもっと効率よく短く簡単に書く方法があるかもしれません。今の私だとこれくらいです。
(-10+5)*(2+3)を計算させてみると以下のような動きをします。
正しく計算されていますね。
ちなみに先ほどのコードで計算部分は主に以下の部分だけです。
StringBuilder sb = new StringBuilder();
DataTable dt = new DataTable();
private void btn_clicked(object sender, EventArgs e)
{
sb.Append(((Button)sender).Text);
txtCbox.Text = sb.ToString();
}
private void btnEqual_Click(object sender, EventArgs e)
{
try
{
rtbDisplay.Text = Convert.ToDecimal(dt.Compute(sb.ToString(), "")).ToString();
}
catch
{
rtbDisplay.Text = "不正な式です。";
}
}
順を追って解説してみます。
まず計算式(文字列)を格納する変数を宣言します。
string変数でも問題ありませんが今回はStringBuilder(クラス)を用いています。
StringBuilder sb = new StringBuilder();
これで文字列を保持できる変数が宣言されました。
次に今回の主役であるDataTableクラスを使用できるようにインスタンス化します。
DataTable dt = new DataTable();
次に計算式を文字列に追加する処理を、以下のbtn_clickedイベントに記述します。
「sb.Append() 」は()内に記述した文字列を足す関数です。
例えば
sb.Append("まるさん");
sb.Append("こんにちは");
と記述すると、sbには「まるさんこんにちは」という文字列が格納されることになります。
今回はStringBuilder.Append関数の括弧の中で、
((Button)sender).Text
を与えています。このsenderとはイベントを発生させたコントロール(テキストボックスやボタンのようなフォームに配置できるやつと思ってください)を意味してます。
.Textは例えばボタンだとそのボタンに表示されている文字列です。
つまりボタン1が押されるとボタン1に記述されている「1」がStringBuilderに足されていきます。
次に
txtCbox.Text = sb.ToString();
これは先ほどのキャプチャの計算式を表示するテキストボックスです。
↓これ
なくても動きますが、例えば以下のように計算式が間違っている時にはエラーがでるので、どこが間違っているかわかりやすいように実装してます。
以下のように「 )」を忘れているため式として不完全でエラーになります。
次にイコールボタンを押下した際のイベントです。
tryとcatchは詳しくは他のブログを参照してもらうとして詳しいことは割愛しますが、
tryの中に記述したコードを実行したときにエラーが起きるとcatch{}に飛びます。
つまりエラーが起き得るコードはtry{}の中に記述し、エラーが起きたときの処理をcatch{}内に記述することになります。
これを記述しないとエラーが発生し、ダイアログが表示されたりしてしまいます。
↓こんな感じです。
計算している部分は以下です。
rtbDisplay.Text = Convert.ToDecimal(dt.Compute(sb.ToString(), "")).ToString();
この一行で文字列を計算し、ディスプレイに表示することができます。
まず計算をしているコアの部分は上の色がついている部分です。
dt.Compute(sb.ToString(), "")
dtはDataTableをインスタンス化するときにつけた名前です。任意につけられます。
このクラス内に用意されている「Compute()」という関数によって文字列を計算することができます。
第一引数に計算する文字列を指定します。
文字列ならばなんでもいいので例えば "1+2" のように文字列ベタ書きでもオッケーです。
第二引数には条件を指定しますが、今回はただの計算なので条件なし("")です。
本来DataTableはSQLなどを用いてSELECTしてきた結果を格納したりして、その格納したデータの条件を指定できたりするのですが、話が脱線しますので割愛します。
Convert.ToDecimal()関数は、簡単に言うと括弧内をDecimal型に変換しますよ という関数です。
Decimalというのはざっくり言えば数字で、「±1.0 x 10-28 から ±7.9228 x 1028」の範囲の数字を扱えます。
要はとんでもない範囲の数字扱えますよと覚えていただければいいんじゃないかなと。
そして最後に計算した結果Decimal型になっている値を.ToString()関数で文字列に直してディスプレイに代入してます。
「rtbDisplay.Text = "不正な式です。";」の部分は先ほどのエラー画面のように、tryの中でエラーが起きたらディスプレイに「不正な式です。」と表示させますよ というコードです。
これで52行という行数に収まっています。
尚、「←」ボタンの部分が
この部分なのですが、これは最悪なくても動きます。
これを省くと41行で書くことができます。
「←」ボタンは式が間違った時などに一文字削除するボタンです。
詳しい説明は割愛します。興味があれば調べてみてください。
いかがでしたでしょうか。
3年前に600行もかけて複雑に組んだコードも3年を経て52行まで圧縮されました。
といっても現場でコーディングほとんどしてこなかったんですけどね 苦笑
配列を使った電卓も考えましたがロジックが複雑になるので断念しました。
よっぽどやるきになったら組んでみようかと思います。(多分やらない)
そんな感じでした。
それではまた!