具体的に質問します。
Editコンポーネントと
Buttonコンポーネント
Labelコンポーネントを一つずつ配置します。
ユーザー側がEditに計算式をキーボ−ドで入力します。
例えば
7+5*3-8
のように。
で、Buttonを押すことでLabelにその結果を表示させるようにしたいのです。
この場合なら
14
のように。
つまり、Editに入力された文字列の中で、
数字と四則演算子の+-*/を区別させて、配列にそれらの要素を格納させて、なおかつどこかで計算させようというのが
私の考えなのですが、
Pos関数や、AnsiPos関数のような、文字列に関係する関数を使ってうまくできないものかと思って考えていますが、なかなかうまく出来ません。
何か良いアイディアあればお願いします。
確か同じような質問が、以前あったと思います。
検索で探してみてください。
ところで、自力でやるとしても、
Pos関数などでは効率が悪いような気がします。
Delphiの文字列型は、配列のように使う(Str[1]など)こともできるので、
Deleteで、解釈が終わるたびに消しつつしながら、
変数をためていくのがいいとおもいますよ。
(実際には括弧とかもあるし、Deleteではむりっぽそうですけど。他いくつかの操作を組み合わせる必要がありそうです)
>Delphiの文字列型は、配列のように使う(Str[1]など)こともできるので、
そんなことできるのですか?
まだDelphi使い始めてまもないので…。
それがあるのなら私の悩みも解決できるかも…。
ちょっと調べてみます。
逆ポーランド記法なら簡単なんですけどね。
# 7+5*3-8であれば、7 5 3 * + 8 -となる。
四則演算は、次のようなBNFで記述できます。
expr: expr '+' expr
| expr '-' expr
| expr '*' expr
| expr '/' expr
| '-' expr
| '(' expr ')'
| NUMBER
;
ただし、'+','-'よりも、'*','/'のほうが優先度が高く、さらに単項負号である'-'(「マイナス1」などの記号。減算の'-'ではない)は一番優先度が高い。
ちなみに、NUMBERに当たる正規表現は、
-?(([0-9]+)|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?)
です。
これを自前で処理させるとしたら至難の業です。
もし、このような字句解析・構文解析に興味がおありなら、Lex/Yaccをおすすめします。
もともとC言語用ですが、
http://www2.big.or.jp/~osamu/Delphi/
こちらに、TPLex, TPYacc(TurboPascal用Lex/Yacc)のDelphi用コードがあります。
サンプルに、計算するものもあります。
う…うーん。なんか高度な話なようで…。
逆ポーランド記法とかBNFとか…。
やっぱり難しいんですかねぇ…。
すいません。もう少し考えてみます。
>> Delphiの文字列型は、配列のように使う(Str[1]など)こともできるので、
> そんなことできるのですか?
Anothor HTML Lintを実行し、その出力文字列の最後の行から、
点数を抜き出します。(おとといつくったばっか)
e,sはInteger。ResultLabel.Captionには最終行の文字列
PlaySoundはうちにあるTPlaySoundコンポーネント。
// 点を検索、点数を取得
e := Pos('点',ResultLabel.Caption); // 終了位置を取得
Dec(e);
s := e;
while ResultLabel.Caption[s] in ['0'..'9','-'] do
Dec(s); // 数字ではなくなるまで、開始位置をデクリメント
case StrToInt(Copy(ResultLabel.Caption,s,e - s + 1)) of
100 : PlaySound.Play('OnPerfect'); // 100点
70..99: PlaySound.Play('OnFully'); // 70点以上
30..69: PlaySound.Play('OnNormal'); // 30点以上
0..29 : PlaySound.Play('OnWrong'); // 30点未満
else PlaySound.Play('OnFailure'); // マイナス点
end;
ほんとはeのときにエラー処理をちゃんとしておきましょう^^;
(定義ファイルが見つからなかったりしたら、採点できないので)
こんにちは、Halbow です。
> つまり、Editに入力された文字列の中で、
> 数字と四則演算子の+-*/を区別させて、配列にそれらの要素を格納させて、
> なおかつどこかで計算させようというのが
実行時に入力された数式を解釈して計算するコンポーネントがあります。
Delphian World
http://home1.infonia.ne.jp/~delphian/delphi/
の Miscellanious のところの
数式解析コンポーネント MathsParser (藤巻氏作)
を使えばできると思います。
遅くなりました。すいません。
皆さん有難うございます。
特に、Halbowさんのおっしゃってくれた数式解析コンポーネント MathsParserは使えそうです。フリーというのも嬉しかったですし。
まだ、このコンポーネント自体の能力や、使い方はイマイチ把握していませんが、
ちょっと調べてみたいと思います。
うまくいきましたら「解決」として報告します。
有難うございます。
すいません。名前間違えました。
きんにくんではなく、ぜいにくんです。
bat や シェルに丸投げしちゃって、テキスト書き出しする。
そのテキストから読み込む・・・という方法もいいかと思いますよ。
+ - */ 四則演算 ¥ 除算 ^ 乗算 , ()あり , */¥^ 優先計算
小数対応なので Extended で値が返ってきます
//Space飛ばす(前方)
procedure SkipSpace(TEXT: String; var Index: Integer);
var
B: Boolean;
TEXT_LENGTH: Integer;
begin
TEXT_LENGTH := Length(TEXT);
B := True;
while (INDEX <= TEXT_LENGTH) and (B = True) do
begin
if (TEXT[Index] = ' ') then Inc(INDEX) else B := False;
end;
end;
//Space飛ばす(後方)
procedure SkipSpace2(TEXT: String; var Index: Integer);
var
B: Boolean;
TEXT_LENGTH: Integer;
begin
TEXT_LENGTH := Length(TEXT);
B := True;
while (INDEX <= TEXT_LENGTH) and (B = True) do
begin
if (TEXT[Index] = ' ') then Dec(INDEX) else B := False;
end;
end;
function GetValue(TEXT: String; var INDEX: Integer; var SELECT_TEXT: String): Boolean;
var
B: Boolean;
SAVE_INDEX: Integer;
TEXT_LENGTH: Integer;
begin
TEXT_LENGTH := Length(TEXT);
if (INDEX <= TEXT_LENGTH) then
begin
SAVE_INDEX := Index;
if (TEXT[INDEX] = '-') or (TEXT[INDEX] = '+') then Inc(INDEX);
if TEXT[INDEX] = '$' then Inc(INDEX);
B := True;
while (INDEX <= TEXT_LENGTH) and (B = True) do
begin
if CharInSet(TEXT[INDEX],[' ','+','-','*','/','\','^']) = True then
begin
B := False;
end
else
begin
Inc(INDEX);
end;
end;
SELECT_TEXT := Copy(TEXT,SAVE_INDEX,INDEX-SAVE_INDEX);
RESULT := True;
end
else
begin
RESULT := False;
end;
end;
function GetValue2(TEXT: String; var INDEX: Integer; var SELECT_TEXT: String): Boolean;
var
B: Boolean;
SAVE_INDEX: Integer;
INDEX2: Integer;
begin
if (0 <= INDEX ) then
begin
SAVE_INDEX := Index;
B := True;
while (0 < INDEX) and (B = True) do
begin
if TEXT[INDEX] = ' ' then
begin
B := False;
end
else
if CharInSet(TEXT[INDEX],['+','-']) = True then
begin
INDEX2 := INDEX - 1;
SkipSpace2(TEXT,INDEX2);
if CharInSet(TEXT[INDEX2],['+','-','*','/','\','^']) = True then
begin
INDEX := INDEX2 + 1;
end;
B := False;
end
else
if CharInSet(TEXT[INDEX],['*','/','\','^']) = True then
begin
B := False;
end
else
begin
Dec(INDEX);
end;
end;
SELECT_TEXT := Copy(TEXT,INDEX+1,SAVE_INDEX-INDEX);
RESULT := True;
Inc(INDEX);
end
else
begin
RESULT := False;
end;
end;
function SelTextToFloat(SELECT_TEXT: String; var Value: Extended): Boolean;
begin
RESULT := True;
try
if Pos('.',SELECT_TEXT) = 0 then
begin
VALUE := Extended(StrToInt(SELECT_TEXT));
end
else
begin
VALUE := StrToFloat(SELECT_TEXT);
end;
except
//数値エラー
RESULT := False;
end;
end;
function LastPos(SubStr,TEXT: String): Integer;
var
Offset: Integer;
begin
RESULT := 0;
Offset := 1;
while Offset <> 0 do
begin
Offset := Pos(SubStr,TEXT,Offset);
if Offset <> 0 then
begin
RESULT := Offset;
Inc(Offset);
end;
end;
end;
function Replace(TEXT: String; I1,I2: Integer; NewText: String): String;
var
S1,S2: String;
begin
S1 := Copy(TEXT,1,I1-1);
S2 := Copy(TEXT,I2);
RESULT := S1 + NewText + S2;
end;
//べき乗
function Power(X: Extended; Y: Integer): Extended;
var
I: Integer;
begin
RESULT := 0;
for I:=1 to Y do
begin
RESULT := RESULT + X;
end;
end;
procedure XXX(Index: Integer; TEXT: String; Operation: Char; var ARESULT: Extended; var ERROR: Integer);
var
TEXT_LENGTH: Integer;
SELECT_TEXT: String;
C: Char;
VALUE: Extended;
begin
if ERROR = 0 then
begin
TEXT_LENGTH := Length(TEXT);
SkipSpace(TEXT,INDEX); //SKIP SPACE 数値前のスペース
//数値取得
if GetValue(TEXT,INDEX,SELECT_TEXT) = true then
begin
if SelTextToFloat(SELECT_TEXT,VALUE) = True then
begin
//-------------------------
// 四則演算 + - * / \
//-------------------------
case Operation of
'+':ARESULT := ARESULT + VALUE;
'-':ARESULT := ARESULT - VALUE;
'/':ARESULT := ARESULT / VALUE;
'*':ARESULT := ARESULT * VALUE;
'\':ARESULT := Extended(Trunc(ARESULT) mod Trunc(VALUE));
'^':ARESULT := Extended(Power(ARESULT,Trunc(VALUE)));
else
ARESULT := VALUE;
end;
SkipSpace(TEXT,INDEX); //SKIP SPACE 数値と四則演算の間のスペース
if INDEX <= TEXT_LENGTH then
begin
C := TEXT[Index];
if (C = '+') or (C= '-') or (C='*') or (C='/') or (C = '\') or (C = '^') then
begin
Inc(INDEX); //Operation の次の位置から
XXX(INDEX,TEXT,C,ARESULT,ERROR);
end
else
begin
//四則演算が + - * / \ ^ 以外
ERROR := 2;
end;
end
else
begin
//テキスト最後
ERROR := 0;
end;
end
else
begin
//ERROR <> 0
end;
end
else
begin
//数値が無い
ERROR := 3;
end;
end
else
begin
//ERROR <> 0
end;
end;
// * と / を計算
function YYY(TEXT: String; var ERROR: Integer): String;
var
C: Char;
INDEX,INDEX1,INDEX2,INDEX3,INDEX4: Integer;
SAVE_INDEX,SAVE_INDEX1,SAVE_INDEX2: Integer;
SELECT_TEXT1,SELECT_TEXT2: String;
E1,E2: Extended;
S1,S2: String;
begin
RESULT := TEXT;
while (Pos('*',TEXT) <> 0) and (ERROR = 0) do
begin
INDEX1 := Pos('*',TEXT);
INDEX2 := Pos('/',TEXT);
INDEX3 := Pos('^',TEXT);
INDEX4 := Pos('\',TEXT);
if (INDEX1 or INDEX2 or INDEX3 or INDEX4) <> 0 then
begin
C := Char(0);
if (INDEX2 or INDEX3 or INDEX4) < INDEX1 then
begin
INDEX := INDEX1;
C := '*';
end;
if (INDEX1 or INDEX3 or INDEX4) < INDEX2 then
begin
INDEX := INDEX2;
C := '/';
end;
if (INDEX1 or INDEX2 or INDEX4) < INDEX3 then
begin
INDEX := INDEX3;
C := '^';
end;
if (INDEX1 or INDEX2 or INDEX3) < INDEX4 then
begin
INDEX := INDEX4;
C := '\';
end;
SAVE_INDEX := INDEX;
//* - の前の値
Dec(INDEX);
SkipSpace2(TEXT,INDEX);
GetValue2(TEXT,INDEX,SELECT_TEXT1);
SAVE_INDEX1 := INDEX;
//* - の後ろの値
INDEX := SAVE_INDEX;
Inc(INDEX);
SkipSpace(TEXT,INDEX);
GetValue(TEXT,INDEX,SELECT_TEXT2);
SAVE_INDEX2 := INDEX;
S1 := Copy(TEXT,1,SAVE_INDEX1-1);
S2 := Copy(TEXT,SAVE_INDEX2);
if C = '^' then
begin
if SelTextToFloat(SELECT_TEXT1,E1) = True then
begin
try
TEXT := S1 + FloatToStr(Power(E2,StrToInt(SELECT_TEXT2))) + S2;
except
ERROR := 99;
end;
end
else
begin
ERROR := 99;
end;
end
else
begin
if SelTextToFloat(SELECT_TEXT1,E1) = True then
begin
if SelTextToFloat(SELECT_TEXT2,E2) = True then
begin
if C = '*' then TEXT := S1 + FloatToStr(E1 * E2) + S2;
if C = '/' then TEXT := S1 + FloatToStr(E2 / E2) + S2;
if C = '\' then TEXT := S1 + FloatToStr(Trunc(E1) mod Trunc(E2)) + S2;
end
else
begin
ERROR := 99;
end;
end
else
begin
ERROR := 99;
end;
end;
end;
end;
RESULT := TEXT;
end;
function ZZZ(TEXT: String; var ERROR: Integer): Extended;
var
Index1,Index2: Integer;
SELECT_TEXT: String;
ARESULT: Extended;
begin
if ERROR = 0 then
begin
while (Pos('(',TEXT) <> 0) and (ERROR = 0) do
begin
Index1 := LastPos('(',TEXT);
if Index1 <> 0 then
begin
Index2 := Pos(')',TEXT,Index1) + 1;
if Index2 <> 0 then
begin
SELECT_TEXT := Copy(TEXT,Index1+1,Index2 - Index1-2); // () 内のテキスト
//* / ^ \ を先行計算
SELECT_TEXT := YYY(SELECT_TEXT,ERROR);
//() 内を計算させる
ARESULT := 0;
ERROR := 0;
XXX(1,SELECT_TEXT,Char(0),ARESULT,ERROR);
//() 内を置き換える
TEXT := Replace(TEXT,Index1,Index2,FloatToStr(ARESULT));
end
else
begin
//)が無い
ERROR := 5;
end;
end;
end;
// () の無くなったテキストを計算
if ERROR = 0 then
begin
RESULT := 0;
//* / ^ \ を先行計算
TEXT := YYY(TEXT,ERROR);
XXX(1,TEXT,Char(0),RESULT,ERROR);
end;
end;
end;
//MEMO1 に式
//MEMO2 に結果
procedure TForm1.Button1Click(Sender: TObject);
var
RESULT: Extended;
ERROR: Integer;
I: Integer;
begin
Memo2.Clear;
for I:=0 to Memo1.Lines.Count -1 do
begin
ERROR := 0;
RESULT := ZZZ (Memo1.Lines[I],ERROR);
if ERROR = 0 then
begin
Memo2.Lines.Add(FloatToStr(RESULT));
end
else
begin
Memo2.Lines.Add('ERROR:' + IntToStr(ERROR));
end;
end;
end;
自分だったら外部スクリプトに計算を投げて結果を受け取る方が早いと思うけどどうでしょ?
一昔前ならDelphiでDelphiをインタプリタ実行する PPA(project-PPA)などを使ってたかも
簡単そうに思われてる人いるかも知れませんがこの出題はコンピュータ系学校の卒論レベルですよ。
ここから逆ポーランド記法にハマったり、この入力が出来る関数電卓に慣れて普通の電卓に戻れなくなったりします。
ツイート | ![]() |