How To Use TFPExpressionParser/fi
│
English (en) │
suomi (fi) │
TFPExpressionParser sallii analysoida ja laskea laskuja kuten sin(x)*cos(2*x)
muuttujan x
millä tahansa arvolla. Matemaattisten lausekkeiden lisäksi se voi käsitellä myös boolean arvoja, merkkijonoja, päivämäärän/ajan arvoja jne. Jopa käyttäjän antamat toiminnot voidaan yhdistää.
Se kuuluu FPC:n Free Component Library (FCL)-kirjastoon ja se on toteutettu käännösyksikössä (unit) fpexprpars.pp
, kansio (fpc_source_dir)/packages/fcl-base/src
. Lisätään vain fpexprpars
uses-lauseseen jotta saadaan pääsy sen toiminnallisuuteen. ´
Katsomalla tiedostoa "fpexprpars.txt" (kohdassa (fpc_source_dir)/packages/fcl-base/examples
) nähdään lyhyt dokumentaatio siitä.
Parserin luominen
Parserin/Jäsentimen voi luoda tällaisella instanssilla:
uses
fpexprpars;
var
FParser: TFPExpressionParser;
begin
FParser := TFpExpressionParser.Create(self);
// ... do something (see below)
Jos tätä kutsutaan lomakkeen metodista, "self" viittaa lomakkeeseen. Koska jäsennin periytyy TComponent
-luokasta, sitä ei tarvitse tuhota nimenomaisesti, koska sen omistaja, lomake, tekee sen. Toisaalta on myös mahdollista luoda jäsennin missä tahansa ohjelmassa ilman lomaketta tai edes luokkaa; käytä tässä tapauksessa [[Nil/fi|nil]]
jäsentimen omistajana, mutta älä unohda .Free
kutsua tällöin käytön jälkeen:
uses
fpexprpars;
var
FParser: TFPExpressionParser;
begin
FParser := TFPExpressionParser.Create(nil);
try
// ... do something (see below)
finally
FParser.Free;
end;
end;
Sisäänrakennetut luokat
Parseri on suunniteltu hyvin joustavasti, mutta oletusparseri on melko tyhmä. Siihen on määritettävä, mitä lausekkeita se hyväksyy. Tämä tehdään lisäämällä vastaava tunniste sisäänrakennettujen luokkien joukkoon. Niihin pääsee parserin ominaisuudella BuiltIns
:
type
TBuiltInCategory = (bcStrings, bcDateTime, bcMath, bcBoolean, bcConversion, bcData, bcVaria, bcUser);
TBuiltInCategories = set of TBuiltInCategory;
Tässä on kokoelma sisäänrakennettuja symboleja, joita voidaan käyttää lisäämällä luokkia parserin BuiltIns
- jokainen, joka "ymmärtää" Pascalia, pitäisi olla selvää, mitä nämä symbolit tarkoittavat ...
- bcStrings:
Length
,Copy
,Delete
,Pos
,Lowercase
,Uppercase
,StringReplace
,CompareText
- bcDateTime:
Date
,Time
,Now
,DayOfWeek
,ExtractYear
,ExtractMonth
,ExtractDay
,ExtractHour
,ExtractMin
,ExtractSec
,Extractmsec
,EncodeDate
,EncodeTime
,ShortDayName
,ShortMonthName
,LongDayName
,LongMonthName
- bcMath:
cos
,sin
,arctan
,abs
,sqr
,sqrt
,exp
,ln
,log
,frac
,int
,round
,trunc
, - bcBoolean:
shl
,shr
,IFS
,IFF
,IFD
,IFI
(TheIFxxx
symbols have the same effect as fpc'sIfThen
for string (IFS
), floating point (IFF
), date/time (IFD
), or integer (IFI
) variables) - bcConversion:
IntToStr
,StrToInt
,StrToIntDef
,FloatToStr
,StrToFloat
,StrToFloatDef
,BoolToStr
,StrToBool
,StrToBoolDef
,DateToStr
,TimeToStr
,StrToDate
,StrToDateDef
,StrToTime
,StrToTimeDef
,StrToDateTime
,StrToDateTimeDef
bcData
, bcVaria
ja bcUser
ei käytetää missään fpexprpars:ssa. Lopussa on ohjeet miten lisätään käyttäjän määrittämiä funktioiia.
Jotta matemaattista lauseketta voidaan käyttää niin valinta bcMath
on lisättävä parserin Builtins
:
FParser.Builtins := [bcMath]; // or FParser.Builtins := FParser.Builtins + [bcMath];
Lausekkeet
Lausekkeet vakioilla
Ensimmäisenä esimerkkinä on, että jäsennin laskee hyvin yksinkertaisen lausekkeen 1+1
.
Ensimmäinen vaihe on kertoa jäsentimelle, mitä lauseketta lasketaan. Tätä tarkoitusta varten on ominaisuus Expression
:
FParser.Expression := '1+1';
Seuraava vaihe on laskea lauseke: vain kutsu Evaluate
tai EvaluateExpression
-
ensimmäinen on funktio, kun taas jälkimmäinen on aliohjelma, joka antaa tuloksen parametrina.
var
parserResult: TFPExpressionResult;
begin
....
parserResult := FParser.Evaluate; // or: FParser.EvaluateExpression(parserResult);
Mikä on se salaperäinen TFPExpressionResult
? Koska parseri on hyvin joustava ja voi käsitellä numeroita, merkkijonoja, päivämääriä / aikoja tai boolean arvoja, on oltava monimutkaisempi tietotyyppi, joka palauttaa laskentatuloksen:
type
TResultType = (rtBoolean, rtInteger, rtFloat, tDateTime, rtString);
TFPExpressionResult = record
ResString : String;
Case ResultType : TResultType of
rtBoolean : (ResBoolean : Boolean);
rtInteger : (ResInteger : Int64);
rtFloat : (ResFloat : TExprFloat);
rtDateTime : (ResDateTime : TDatetime);
rtString : ();
end;
Jäsen ResultType
-signaalit, jonka vain yksi tietokentistä on voimassa. On tärkeää ymmärtää tämä, koska parseri on hyvin tiukka tietotyyppien suhteen.
Esimerkissä lisätään kaksi kokonaislukua, joten tulos on myös kokonaisluku. Jos toisaalta olisimme käyttäneet lauseketta "1.0 + 1"
niin ensimmäinen numero olisi ollut reaaliluku ja tulos olisi ollut reaaliluku! Siksi on aina tarkasteltava TFPExpressionResult
-jäsenen ResultType
-tyyppiä ennen tuloksen valintaa. Lausekkeen tulosdatatyypin käytön yksinkertaistamiseksi fpexprpars
paljastaa funktion ArgToFloat
, joka saa koko lausekkeen tulos tietueen parametriksi ja valitsee oikean komponentin, jos reaaliluku tulosta odotetaan:
var
parserResult: TFPExpressionResult;
resultValue: Double;
...
parserResult := FParser.Evaluate; // or: FParser.EvaluateExpression(parserResult);
resultValue := ArgToFloat(parserResult);
Expression
-lausekkeelle.Lauseke muuttujalla
Tässä esimerkissä lasketaan sin(x)*cos(2*x)
jossa x = 0.5
(0,5).
Muuttujien määrittäminen
Aluksi on määriteltävä muuttujat. On vain yksi x
. Parserilla on metodi AddFloatVariable
, joka ilmoittaa reaaliluvun; on myös menetelmiä:
AddBooleanVariable
AddStringVariable
AddDateTimeVariable
boolean-, merkkijono- ja päivämäärä/aika-muuttujille, vastaavasti.
Jokainen näistä aliohjelmista odottaa muuttujan nimeä sekä sen oletusarvoa. Näyte funktiolle sin(x)*cos(2*x)
kutsutaan vain:
FParser.Identifiers.AddFloatVariable('x', 0.5);
0.5
(0,5) syötetään tähän oletusarvoksi, koska se on argumentti, jolla halutaan laskea lauseke (alla näytetään, miten muuttujaa muutetaan). Tästä lähtien jäsennin käyttää tätä numeroa aina, kun se löytää lausekkeessa olevan muuttujan x
.
Voit tietysti lisätä muita nimiä, esim. vakiota, kuten e
jne. (Pii (pi
) on jo sisäänrakennettu).
Lausekkeen määrittäminen
Seuraavassa vaiheessa lausekkeen merkkijono lähetetään parserille:
FParser.Expression := 'sin(x)*cos(2*x)';
On välttämätöntä kutsua tätä muuttujien asettamisen jälkeen, koska jäsentimen on tiedettävä muuttuja lausekkeen analysoimiseksi.
Lausekkeen laskeminen
Tämä tehdään kuten aikaisemmin vakio lausekkeella - tässä on täydellinen aliohjelma, joka näyttää yhtälön ja sen tuloksen viestiruudussa (ShowMessage):
var
FParser: TFPExpressionParser;
resultValue: Double;
begin
FParser := TFPExpressionParser.Create(nil);
try
FParser.BuiltIns := [bcMath];
FParser.Identifiers.AddFloatVariable('x', 0.5);
FParser.Expression := 'sin(x)*cos(2*x)';
resultValue := FParser.Evaluate.ResFloat; // or: resultValue := ArgToFloat(FParser.Evaluate);
ShowMessage(FParser.Expression + ' = ' + FloatToStr(resultValue));
finally
FParser.Free;
end;
end;
Muuttujien muuttaminen
Toistaiseksi x
:llä on aina arvo 0,5 - se käyttäytyy kuin vakio, oltaisiin voitu käyttää myös lauseketta "sin(0.5)*cos(2*0.5)"
.
Jotta tämä käyttäytyisi enemmän kuin "muuttuja", lasketaan nyt testi funktion x
-arvoja välillä -10 ja 10.
Tärkein kysymys on: Miten määrätty arvo korvataan muuttujalla? On useita mahdollisuuksia - kaikki ne edellyttävät sisäistä muuttujaa Identifier
(tyyppi TFPExprIdentifierDef
), joka paljastaa erilaisia tapoja käyttää muuttujia ja niiden ominaisuuksia:
- Käytetään
AddFloatVariable
funktion palautusarvoa. - Haetaan tunniste kutsumalla
FindIdentifierByName
-tunnus muuttujan nimellä parametrina. - Haetaan tunnisteesta
Identifiers
jäsentimen tunnisteiden kokoelmasta käyttämällä muuttujan tunnettua indeksiä: On lisättyx
ainoana muuttujana, joten sen on oltava indeksissä 0.
Kun Identifier
on tunnettu, muuttujan arvoa voidaan muuttaa avaamalla ominaisuus AsFloat
(tai AsDateTime
jne. Vastaavasti):
var
FParser: TFPExpressionParser;
argument: TFPExprIdentifierDef;
s: string;
x: integer;
f: double;
begin
s:='';
FParser := TFPExpressionParser.Create(nil);
try
// Enable the use of mathematical expressions
FParser.BuiltIns := [bcMath];
// Add the function argument
argument := FParser.Identifiers.AddFloatVariable('x', 0.0);
// Define the function, using the argument name x as defined above
FParser.Expression := 'sin(x)*cos(2*x)';
// Calculate some function values
for x := -10 to 10 do
begin
argument.AsFloat := x; // Set the function argument value
f := FParser.Evaluate.ResFloat; // Calculate the function value
s := s + format('x[%d]:[%g]' + LineEnding, [x,f]); // Demo output to display the result
end;
// Show the result
ShowMessage(s);
finally
FParser.Free;
end;
end;
Käyttäjän määrittämien funktioiden lisääminen
Oletusparseri tietää vain edellä mainitut sisäänrakennetut funktiot. Yksi parserin vahvuuksista on se, että on hyvin helppo ulottaa muita funktioita. Tämä voidaan tehdä kutsumalla metodia Identifiers.AddFunction
, esim.
FParser.Identifiers.AddFunction('tan', 'F', 'F', @ExprTan);
Tässä esimerkissä lisätään funktio tan(x) määrittelemällä sen nimi, koska se kutsutaan funktion lausekkeissa (ensimmäinen parametri), tulosarvojen tyyppi (toinen parametri, "F" = reaaliluku, "I" = kokonaisluku, "D" = päiväys/aika, "S" = merkkijono tai "B" = boolean) ja syöttöarvojen tyyppi (kolmas parametri, sama logiikka). Jos funktio hyväksyy useita syöttöparametreja, on määritettävä kunkin tyyppi, esim. "FF" kahdella reaaliluvun arvolla tai "FI" ensimmäinen reaaliluku ja toinen on kokonaisluku parametri. Viimeinen parametri osoittaa funktion, jota kutsutaan aina, kun lausekkeen merkkijonossa on "tan". Koska tällä toiminnolla on tietty syntaksi, on se tehtävä omassa lähdekoodissa:
procedure ExprTan(var Result: TFPExpressionResult; Const Args: TExprParameterArray);
var
x: Double;
begin
x := ArgToFloat(Args[0]);
Result.resFloat := tan(x);
end;
Laskennan tulos palautetaan parametrina "Result"
, joka on edellä kuvattu TFPExpressionResult
. Laskennan argumentit siirtyvät Args
ille, joka on vain joukko TFPExpressionResult-arvoja - koska parametreilla voi olla useita tietotyyppejä. Termi TFPExpressionResults on ehkä hieman harhaanjohtava, koska tämä taulukko sisältää kaikki * tulo * -parametrit, jotka on määritelty AddFunction-metodin tulotyypeillä.