How To Use TFPExpressionParser/fi

From Free Pascal wiki


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 (The IFxxx symbols have the same effect as fpc's IfThen 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.

Note-icon.png

Huomaa: Nämä symbolit eivät ole iso/pieni kirjain kohtaisia.

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);
Note-icon.png

Huomaa: Reaaliluvuilla on oltava piste desimaalierottimena, ei pilkku, jota käytetään joissakin Euroopan maissa. Jos lausekkeen merkkijono tulee käyttäjän syöttämästä lausekkeesta ja sisältää desimaalilukuja, niin on korvattava pilkku pisteillä ensin ennen sen määrittämistä parserin 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 tunnisteestaIdentifiers jäsentimen tunnisteiden kokoelmasta käyttämällä muuttujan tunnettua indeksiä: On lisätty x 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 Argsille, 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ä.