文字列を式として評価して結果を返す(eval関数) ~Delphiソースコード集
TBindingExpressionを使うと入力した文字列、例えば "(10+3)*4/3" を式として評価して結果を返すことができます。
しかし、E(定数)、Sqrt、Sin、Cos、Tan、Abs、Exp、Log10、Pow、Round、RoundUp、RoundDown等は使えないので拡張して使ってみます。
参考URL
https://qiita.com/pik/items/e888e52b4fa2f70a419f
https://qiita.com/pik/items/8384a7b9a04f3e628e71
プロジェクトの作成
[ファイル]⇒[新規作成]⇒[Windows VCL アプリケーション -Delphi] をクリックします。
TEditと、TMemoを配置します。
ソースコードの記述
Edit1をクリックし、左下ペインの「オブジェクト インスペクタ」の「イベント」タブを選択します。
「OnKeyPress」の右側の空白をダブルクリックしてソースコードを記述します。
(Edit1がアクティブで、キーボードのキーを押したとき)
全体のソースコードは以下になります。
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; Memo1: TMemo; procedure Edit1KeyPress(Sender: TObject; var Key: Char); private { Private 宣言 } public { Public 宣言 } end; var Form1: TForm1; function eval(st:String):String; implementation {$R *.dfm} uses System.Bindings.Helper, System.Bindings.Expression, System.Bindings.EvalSys, System.Bindings.EvalProtocol, System.Bindings.Methods, System.Math; function eval(st:String):String; var be : TBindingExpression; DSE:TDictionaryScope; SSqrt,SSin,SCos,STan,SAbs:TPairScope; SExp,SLog10,SPow:TPairScope; SRound,SRoundUp,SRoundDown:TPairScope; begin //Pi 円周率はデフォルトで定数定義されている //E 自然対数の底を定数定義する DSE := TDictionaryScope.Create; DSE.Map.Add('E', TValueWrapper.Create(Exp(1))); //Sqrt(平方根) SSqrt := TPairScope.Create( 'Sqrt', MakeInvokable( function(Args: TArray<IValue>): IValue var V:Extended; begin V := Args[0].GetValue.AsExtended; V := Sqrt(V); Result := TValueWrapper.Create(V); end ) ); //Sin サイン SSin := TPairScope.Create( 'Sin', MakeInvokable( function(Args: TArray<IValue>): IValue var V:Extended; begin V := Args[0].GetValue.AsExtended; V := Sin(V); Result := TValueWrapper.Create(V); end ) ); //Cos コサイン) SCos := TPairScope.Create( 'Cos', MakeInvokable( function(Args: TArray<IValue>): IValue var V:Extended; begin V := Args[0].GetValue.AsExtended; V := Cos(V); Result := TValueWrapper.Create(V); end ) ); //Tan タンジェント STan := TPairScope.Create( 'Tan', MakeInvokable( function(Args: TArray<IValue>): IValue var V:Extended; begin V := Args[0].GetValue.AsExtended; V := Cos(V); Result := TValueWrapper.Create(V); end ) ); //Abs 絶対値 SAbs := TPairScope.Create( 'Abs', MakeInvokable( function(Args: TArray<IValue>): IValue var V:Extended; begin V := Args[0].GetValue.AsExtended; V := Abs(V); Result := TValueWrapper.Create(V); end ) ); //Exp e(自然対数)の X 乗) SExp := TPairScope.Create( 'Exp', MakeInvokable( function(Args: TArray<IValue>): IValue var V:Extended; begin V := Args[0].GetValue.AsExtended; V := Exp(V); Result := TValueWrapper.Create(V); end ) ); //lLog10 10を底とする対数 SLog10 := TPairScope.Create( 'Log10', MakeInvokable( function(Args: TArray<IValue>): IValue var V:Extended; begin V := Args[0].GetValue.AsExtended; Result := TValueWrapper.Create(Log10(V)); end ) ); //Pow(b, e) bのe乗 SPow := TPairScope.Create( 'Pow', MakeInvokable( function(Args: TArray<IValue>): IValue var V1,V2:Extended; begin V1 := Args[0].GetValue.AsExtended; V2 := Args[1].GetValue.AsExtended; Result := TValueWrapper.Create(Power(V1,V2)); end ) ); //Round(v, [d=0]) vのd桁目を四捨五入 SRound := TPairScope.Create( 'Round', MakeInvokable( function(Args: TArray<IValue>): IValue var V1:Extended; V2:Integer; begin V1 := Args[0].GetValue.AsExtended; if Length(Args)=2 then V2 := Args[1].GetValue.AsInteger else V2:=0; Result := TValueWrapper.Create(RoundTo(V1,V2)); end ) ); //RoundUp(v, [d=0]) vのd桁目を切り上げ SRoundUp := TPairScope.Create( 'RoundUp', MakeInvokable( function(Args: TArray<IValue>): IValue var V1:Extended; V2:Integer; begin V1 := Args[0].GetValue.AsExtended; if Length(Args)=2 then V2 := Args[1].GetValue.AsInteger else V2:=0; V1:=Ceil(V1/Power(10,V2)); V1:=V1*Power(10,V2); Result := TValueWrapper.Create(V1); end ) ); //RoundDown(v, [d=0]) vのd桁目を切り捨て SRoundDown := TPairScope.Create( 'RoundDown', MakeInvokable( function(Args: TArray<IValue>): IValue var V1:Extended; V2:Integer; begin V1 := Args[0].GetValue.AsExtended; if Length(Args)=2 then V2 := Args[1].GetValue.AsInteger else V2:=0; V1:=Floor(V1/Power(10,V2)); V1:=V1*Power(10,V2); Result := TValueWrapper.Create(V1); end ) ); be := TBindings.CreateExpression( [DSE,SSqrt,SSin,SCos,STan,SAbs,SExp,SLog10,SPow,SRound,SRoundUp,SRoundDown], st ); try try result:=be.Evaluate.GetValue.AsExtended.ToString; except result:='err'; end; finally be.Free; end; end; procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); begin if Key=#13 then begin Memo1.Lines.Add(Eval(Edit1.Text)); end; end; end.
実行する
実行して、Edit1に、
Sin( 45/180*Pi )
を入力して、キーボードのEnterキーを押すと計算結果がMemo1に表示されます。