Delphi入門

電卓の作成

何故ゆえに「電卓」を作成するんでしょうか?

さて、「電卓」も比較的プログラム初期段階で作成するものですが、 テクニックなど新しい技術を覚えてください。一応、「タイピングソフト作成講座」を閲覧してから御覧になって下さい。 これを修得した上で話を進めて行きます。できればタイピング講座をすべて見てプログラムを実装した上で話を進めて行きます。

何故「電卓」かと言いますと、色々修得へのショートカットであると判断しているからであります。

  • グローバル変数の扱いに慣れる
  • 演算子の型と、演算子の使い方に慣れる
  • グループ化されたコンポーネントの一括処理
  • Case文の使い方を勉強する

電卓では主に数値演算がメインになって来ますが、前回入力した値などを記憶しておくにはどのようにすればいいのか? などなどを扱って行きたいと思います。

フォームのデザイン

では、まず最初にいつも通り、 フォームのレイアウトを作成してみましょう!取りあえずここでは以下のようなNameで扱って行きます。 別に「Button1」とかでも面倒な方はかまいません。

  1. 数値ボタン「0」〜「9」までを作り、それぞれのNameを「btn0」〜「btn9」とします。
  2. 「+」「−」「×」「÷」ボタンを作り、それぞれのNameを「btnA」「btnB」「btnC」「btnD」とします。
  3. 「.」(小数点)を作り、Nameを「btnPoint」にします。
  4. 数値表示用のTEditを配置します。Nameは「EditCalc」にします。Textは「0」にしておきます。
  5. 「C」「AC」ボタンを付け加えて、Nameには、「btnClear」「btnAllClear」とします。
  6. 「=」ボタンは、「btnResult」とします。

フォームを自分の好きな用に作成して下さい。 自分で電卓っぽく作って見て下さい。重要なのは、後で解説しますが、『数値ボタン「0」〜「9」までを作り、 それぞれのNameを「btn0」〜「btn9」とします。』というものです。「btnx」で「x」には必ず対応する数字を指定して下さい。 別に「Button8」とかでも構いませんので、対応させて下さい。

電卓・レイアウト例

このような感じにしてみました。 デザインがダサいとかいう方は色々変更して下さい。 ただ、これは1分ほどでレイアウトをデザインしたものなのでそこらへんは御了承下さいませ(笑)。

今は、基本的なもので話を進めて行きますけど、 後で関数電卓のように「log」とか「ルート」とかも入れてもいいですね。 ちなみに使っているコンポーネントは詳しく説明しませんけど、

  • TButton × 17
  • TEdit × 1
  • TBevel × 2

ボタンでなくてもパネルとかでもできる方や デザインももう少し微妙に調整したい方ははそうして頂いても全然OKです。

グローバル変数

さて、今まで変数というものは何となく理解できてきたと思いますが、 例えば「Button1」で宣言した変数は、そのイベントが終わってしまったら解放されてしまいます。 何を言っているのかといいますと、「Button1」で例えば「x」という変数を使った場合、 それはそのイベント内(OnClickならOnClick内)でしか使えませんよね? しかし、とある状況では、「あ〜、あの時使った変数をここでもう一度使えないかな?」 というシチュエーションがしばしば出てくる事があります。

今まで見て来た変数は「ローカル変数」と呼ばれるものです。 一般に「変数」といいますと「ローカル変数」の事を指しますが、 「グローバル変数」というものは、どこからでも呼び出せる変数です。 「ローカル(local)」:地方、「グローバル(global)」:世界的という英単語にもピッタリですね。 この電卓ではこのグローバル変数が必須になってきます。 まあ、使わない方法もあるんですが、こちらに馴染んで置いた方が良いでしょう。

「btn0」〜「btn9」に書くプログラムは?

「btn0」〜「btn9」に書くプログラムは 「0」〜「9」と書かれたボタンを押すだけのプログラムなので簡単です。 電卓を実際に思い浮かべて見て下さい。表示欄に押したボタンがどんどん記入されます… あれと同じ事をやりますが、注意しなくてはならないのは、 もし「0」から始まった場合はきちんと考慮しなくてはならない事くらいでしょうか。 ちょっと考えれば簡単です。「btn1」〜「btn9」までは以下のプログラムで行けそうなのはお分かりでしょうか?

procedure TForm1.btn5Click(Sender: TObject);
begin if EditCalc.Text <> '0' then EditCalc.Text := EditCalc.Text + '5' else EditCalc.Text := '5' ; end; end ;

OKなのはお分かりでしょうか? ただ、まだ実装しないで下さい!但し、最後は文字列としての「5」という意味です。 それを+しているので、現在表示されているEditCalcの内容の最後に押したボタンの値をそのまま付け加えている事になります。 「btn0」には次のプログラムを書きます。これもよく見ればお分かりかと思います。 表示されているものが「0」だった場合は電卓では押した文字になりますので、これも適応しているワケですね。 なお、「0」だけは特殊です。「0」というものは最初が0だったら連続で0とは打てません。 しかし、最初に「EditCalc.Text <> '0'」というものがありますので、これを「btn0」に書いても大丈夫なワケです。

「btn0」〜「btn9」に書くプログラムは? » 一括コントロール

VisualBasicの経験がおありの方は御存知のコントロール配列というものがあります。 あれを「Delphi」でも使ってみたいと思います。 このように、同じようなものに同じようなプログラムを書くのは非常に面倒ですし、 ちょっと間違えた時などいちいち一個一個修正しなくてはなりません。そこで次のような手段を使います!

電卓・コンポーネント選択

まず、0から9までのボタンを全て選択します。 「Shift」を押しながらひとつづつクリックすればできます。そしてダブルクリックして下さい。 ここでは「btn0」クリックが基準になっていますが、これは「btn0」を最初にクリックしたからで別にどれでも構いません。

ここで重要なのは「Sender」です。「Send」というのは英語では「送る」という意味ですが、 ここでは引数みたいになっています。どういう事かと言いますと、このイベントが起きる時には、 引数「Sender」が渡されている事になります。簡単に言いますと、 どのコンポーネントからこのプログラムが実行されたか?というものを意味します。 つまり、これを利用して次のように書けます。

procedure TForm1.btn0Click(Sender: TObject);
var s : Char ;
begin
   if Sender is TButton then
   begin
      s := TButton(Sender).Name[4] ;
      if EditCalc.Text <> '0' then EditCalc.Text := EditCalc.Text + s
            else EditCalc.Text := s ;
      (*
         この部分は次のように書き換えるとスマートになります。
         with EditCalc do
            if Text <> '0' then Text := Text + s else Text := s ;
      *)
   end ;
end;

何やらいきなりでは難しいですね。 でも、こうやるとラクです。これが意味全然分からないという方は前のようにひとつひとつボタンに書いて言った方が無難です。 これは何を意味しているのかと言いますと、押されたボタンをまず判定しています。

例えば、「8」ボタンを押しますと、 「Sender」には「btn8」が渡されて実行されます。 そして、「Sender」は「TButton」なので次にその名前の4番目の文字を取り出します。 この場合「btn8」の4番目は「8」ですね。つまり、「Button8」にしている方は4番目ではなく、7番目にします。 そして後は同じように書いています。これにより、一括して処理できるようになります。 「Char」というのは、「String」型とは違い、文字列1文字を収納しておくための型です。

小数点への対応

小数点というものはなかなか厄介です。 もしすでに小数点が打たれていた場合には、打てなくし、そうでなければ小数点を打つ…という事をしますが、 ここでは新しい関数「Pos」というものが出てきます。以下のプログラムは「btnPoint」の「OnClick」イベントです。

procedure TForm1.btnPointClick(Sender: TObject);
begin
   if Pos('.',EditCalc.Text) = 0 then
      EditCalc.Text := EditCalc.Text + '.' ;
end;

となります。一行で書けますが、新しい関数が出てきました「Pos」です。これは何かと言いますと、

Pos(検索文字列,対象文字列) → 戻り値は、Integer型

です。今回の場合ですと、「EditCalc.Text」というのは現在表示されているものです。その文字列の中に「.」(小数点)があれば…という事を意味しています。 もし、文字列中に検索文字列が見つからなければ「0」という値を返します。従って、この判定文の意味する所は、

  • 表示欄の中に小数点が入っていれば後尾に「0」を追加させる

となります。すでにある場合には、その小数点のある位置(何バイト目にあるか)が返されるので、何も実行されない事になります。

グローバル変数の利用、四則演算子ボタンを押した時の判定

四則演算子とは「+」「−」「×」「÷」の事です。今まではサクッといけたと思いますが、ここからが本番です。 まず、グローバル変数は先程もお話した通りです。 例えば電卓では「+」ボタンを押して、次に値を入力すると、前入力したものはクリアされて(正確には保存されて) 新しい値を記入します。これを思い浮かべる事が出来れば十分です。

そこで、これは通常の変数では前に入力した値を扱う事はできないのでグローバル変数を使います。 グローバル変数は、プログラムの一番上まで言って見て下さい。 「private」「public」と書かれている所がありませんか?ここに宣言します。 「private」はそのフォーム内だけで使うとき、 「public」は他のフォームも健在している時に現在のフォームで指定している変数を扱いたい時にしますが、 今回の場合は時にどちらでも構いません。お好きな方に通常の変数を指定するのと同じように宣言して下さい。 「var」は入りません。

private
   value : Extended ;
   ope : Char ;

ここで、「Extended」というものが出てきました! これは「Integer」だけでは「整数」しか扱う事はできません。 電卓では当然小数点の計算もできなくてはなりませんので、このように小数点を含んだ型を利用します。 「Extended」をヘルプで調べて見て下さい。色々書かれていると思います。 また、専門的には「浮動小数点を扱う時の型」などといいます。

分かりやすくいいますと、電卓の中にもメモリが存在しています。 「+」ボタンを押した事を記憶しておき、「=」を押すと、記憶を取り出して、 現在表示されている値と記憶されている値を足します。つまり、この記憶(メモリ)的役割を持っているのが、 ここで定義されている変数ですね。

「ope」という変数には「+」「-」「*」「/」というものを保存しておきます。 「=」ボタンが押された時に、以前にどのボタンが押されていたかを確かめるための変数です。 「char」というのは「キャラ型」といいます。「String」が長い文字列を保持できたのに対して、 「char」は「1バイトの文字」を格納しておけます。メモリ消費にも優しくなります。 まあ、この程度ですど、殆ど変わりませんけど、できるだけ無駄なくプログラムを美しく書くにはこういう考え方も大事だと思います。

演算子

演算子とは「+」「−」「×」「÷」などの事です。 他にも色々ありますが、ここではこの4つを扱って行く事にします。 しかも、これらは簡単です。PCではどのように扱うのでしょうか?それは次のように扱います。

procedure TForm1.button1click(sender : tobject) ;
var x,y,z,w : Extended ;
begin
   x := 5 + 2 ;
   y := 5 - 2 ;
   z := 5 * 2 ;
   w := 5 / 2 ;
   ShowMessage(FloatToStr(x) + ',' + FloatToStr(y) + ',' +
      FloatToStr(z) + ',' + FloatToStr(w)) ;
end ;

とこんな感じではどうでしょうか。これで、結果は"7,3,10,2.5"となります。 型を変換(小数点→文字列)の時には「IntToStr」に対して「FloatToStr」という命令を使います。 尚、変数xとyとzだけなら、変数宣言の際には「Integer」でも構いませんが、 「/」の記号を使った場合は例え結果が整数値だとしても、小数点系の型を宣言しなくてはいけない事に注意して下さい。

総仕上げ

それでは以上をふまえて早速書いて見ましょう。 まず、考え方は分かるでしょうか?グローバル変数を使います。「+」(btnA)ボタンが押されたら、 現在電卓上に表示されている数値をグローバル変数に代入する。 もし、グローバル変数が前に値を持っていればその値と電卓の現在値を足し合わせてグローバル変数に再度値を入れ直します。

procedure TForm1.btnAClick(Sender : TObject) ;
begin
   ope := '+' ;
end ;

procedure TForm1.btnBClick(Sender : TObject) ;
begin
   ope := '-' ;
end ;

procedure TForm1.btnCClick(Sender : TObject) ;
begin
   ope := '*' ;
end ;

procedure TForm1.btnDClick(Sender : TObject) ;
begin
   ope := '/' ;
end ;

としておきます。そして、最後に、これらで押されたボタンを記憶しておきます。 それで、「=」ボタンには次のコードを書きます。

procedure TForm1.btnResultClick(Sender : TObject) ;
begin
   Case ope of
      '+' : value := value + StrToFloat(EditCalc.Text) ;
      '-' : value := value - StrToFloat(EditCalc.Text) ;
      '*' : value := value * StrToFloat(EditCalc.Text) ;
      '/' : value := value / StrToFloat(EditCalc.Text) ;
   end ;
   EditCalc.Text := FloatToStr(value) ;
end ;

「Case」文というのが出て来ました。 さて、これは何かと言いますと、「if」文の強化版です。 "if ope = '+' then"とずらずら判定を書くのをこのように書くことができます。 しかし、この場合「Case」文で判定している変数「ope」なんですが、 「Char型」「Int型」などと判別できるものが限定されていますので、うまく利用して下さい。 詳しくはヘルプにも書いてありますので、御覧下さいね。

課題

さて、完成しました。 しかし、実際に使ってみると色々不都合が出てきます。 ここでは「btnClear」と「btnAllClear」は書きませんでしたが、大丈夫でしょうか? "value := 0 ;"と書けばOKですね。つまり、今まで記憶されている値を「0」にしてやれば言い訳です。 それと同時に「All Clear」の場合はそれに加えて「EditCalc = '0' ;」とすれば全初期化になりますので ここも大丈夫でしょう。電卓と後違う所は…

  1. 「+」ボタンなどを押してしまうと、電卓では端っこに「+」などと記入されているが、この場合だと「0」に。
  2. 「0」で割り算を行うと例外が発生してしまう。

というのがあります。 これにはどのように対処すればいいのでしょうか?最初は、自分で考えて見ましょう。 「+」などの画像を作ってもおもしろいでしょう。それで、「TImage」コンポーネントと連動させたり、 面倒な場合は、「TLabel」を用意してボタンイベントに、 押されたボタンの内容を表示するようにするのがラクだと思います。 後者はヘルプを見て下さい。

結果に「∞」とか出すと、ちょっと変わった電卓もできますが、 その場合は、グローバル変数に「∞」という文字列を渡す事(型変換)はできませんので、 注意が必要です。"if EditCalc = '∞' then"などと書かなくてはなりませんし、小数点ボタンを押すと、 挙動不審になります。このようなバグは自分で見つけて自分で潰して下さい。分からなければヘルプを見るクセを付けましょう。 プログラミングの勉強は自分で調べない事には力は絶対付きませんので頑張って下さい。