How To Use TFPExpressionParser/fi: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: How To Use TFPExpressionParser/fi
| Old | New | Line | |
|---|---|---|---|
| 1 | + | TFPExpressionParser sallii analysoida ja laskea [[expression/fi|laskuja]] kuten `sin(x)*cos(2*x)` [[Variable/fi|muuttujan]] `x` millä tahansa arvolla. Matemaattisten lausekkeiden lisäksi se voi käsitellä myös [[Boolean/fi|boolean]] arvoja, merkkijonoja, [[TDateTime/fi|päivämäärän/ajan]] arvoja jne. Jopa käyttäjän antamat toiminnot voidaan yhdistää. | |
| 2 | + | ||
| 3 | + | Se kuuluu [[FPC/fi|FPC]]:n [[FCL/fi|Free Component Library (FCL)]]-kirjastoon ja se on toteutettu [[Unit/fi|käännösyksikössä (unit)]] `fpexprpars.pp`, kansio `(fpc_source_dir)/packages/fcl-base/src`. Lisätään vain `fpexprpars` [[Uses/fi|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ä. | |
| 4 | + | ||
| 5 | + | ## Parserin luominen | |
| 6 | + | ||
| 7 | + | Parserin/Jäsentimen voi luoda tällaisella instanssilla: | |
| 8 | + | ||
| 9 | + | ``` pascal | |
| 10 | + | uses | |
| 11 | + | fpexprpars; | |
| 12 | + | ||
| 13 | + | var | |
| 14 | + | FParser: TFPExpressionParser; | |
| 15 | + | begin | |
| 16 | + | FParser := TFpExpressionParser.Create(self); | |
| 17 | + | // ... do something (see below) | |
| 18 | + | ``` | |
| 19 | + | ||
| 20 | + | Jos tätä kutsutaan [[TForm/fi|lomakkeen]] [[Method/fi|metodista]], "[[Self/fi|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 [[Program/fi|ohjelmassa]] ilman lomaketta tai edes [[Class/fi|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: | |
| 21 | + | ||
| 22 | + | ``` pascal | |
| 23 | + | uses | |
| 24 | + | fpexprpars; | |
| 25 | + | ||
| 26 | + | var | |
| 27 | + | FParser: TFPExpressionParser; | |
| 28 | + | begin | |
| 29 | + | FParser := TFPExpressionParser.Create(nil); | |
| 30 | + | try | |
| 31 | + | // ... do something (see below) | |
| 32 | + | finally | |
| 33 | + | FParser.Free; | |
| 34 | + | end; | |
| 35 | + | end; | |
| 36 | + | ``` | |
| 37 | + | ||
| 38 | + | ## Sisäänrakennetut luokat | |
| 39 | + | ||
| 40 | + | 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 [[Identifier/fi|tunniste]] sisäänrakennettujen luokkien joukkoon. Niihin pääsee parserin [[Property/fi|ominaisuudella]] `BuiltIns`: | |
| 41 | + | ||
| 42 | + | ``` pascal | |
| 43 | + | type | |
| 44 | + | TBuiltInCategory = (bcStrings, bcDateTime, bcMath, bcBoolean, bcConversion, bcData, bcVaria, bcUser); | |
| 45 | + | TBuiltInCategories = set of TBuiltInCategory; | |
| 46 | + | ``` | |
| 47 | + | ||
| 48 | + | Tässä on kokoelma sisäänrakennettuja symboleja, joita voidaan käyttää lisäämällä luokkia parserin `BuiltIns` - jokainen, joka "ymmärtää" [[Pascal/fi|Pascalia]], pitäisi olla selvää, mitä nämä symbolit tarkoittavat ... | |
| 49 | + | ||
| 50 | + | - **bcStrings**: `Length`, `Copy`, `Delete`, `Pos`, `Lowercase`, `Uppercase`, `StringReplace`, `CompareText` | |
| 51 | + | - **bcDateTime**: `Date`, `Time`, `Now`, `DayOfWeek`, `ExtractYear`, `ExtractMonth`, `ExtractDay`, `ExtractHour`, `ExtractMin`, `ExtractSec`, `Extractmsec`, `EncodeDate`, `EncodeTime`, `ShortDayName`, `ShortMonthName`, `LongDayName`, `LongMonthName` | |
| 52 | + | - **bcMath**: `cos`, `sin`, `arctan`, `abs`, `sqr`, `sqrt`, `exp`, `ln`, `log`, `frac`, `int`, `round`, `trunc`, | |
| 53 | + | - **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) | |
| 54 | + | - **bcConversion**: `IntToStr`, `StrToInt`, `StrToIntDef`, `FloatToStr`, `StrToFloat`, `StrToFloatDef`, `BoolToStr`, `StrToBool`, `StrToBoolDef`, `DateToStr`, `TimeToStr`, `StrToDate`, `StrToDateDef`, `StrToTime`, `StrToTimeDef`, `StrToDateTime`, `StrToDateTimeDef` | |
| 55 | + | ||
| 56 | + | `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. | |
| 57 | + | ||
| 58 | + | > **Note:** Nämä symbolit eivät ole [[case-sensitive/fi|iso/pieni kirjain kohtaisia]]. | |
| 59 | + | ||
| 60 | + | Jotta matemaattista lauseketta voidaan käyttää niin valinta `bcMath` on lisättävä parserin `Builtins`: | |
| 61 | + | ||
| 62 | + | ``` pascal | |
| 63 | + | FParser.Builtins := [bcMath]; // or FParser.Builtins := FParser.Builtins + [bcMath]; | |
| 64 | + | ``` | |
| 65 | + | ||
| 66 | + | ## Lausekkeet | |
| 67 | + | ||
| 68 | + | ### Lausekkeet vakioilla | |
| 69 | + | ||
| 70 | + | 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`: | |
| 71 | + | ||
| 72 | + | ``` pascal | |
| 73 | + | FParser.Expression := '1+1'; | |
| 74 | + | ``` | |
| 75 | + | ||
| 76 | + | Seuraava vaihe on laskea lauseke: vain kutsu `Evaluate` tai `EvaluateExpression` - | |
| 77 | + | ||
| 78 | + | ensimmäinen on [[Function/fi|funktio]], kun taas jälkimmäinen on [[Procedure/fi|aliohjelma]], joka antaa tuloksen parametrina. | |
| 79 | + | ||
| 80 | + | ``` pascal | |
| 81 | + | var | |
| 82 | + | parserResult: TFPExpressionResult; | |
| 83 | + | begin | |
| 84 | + | .... | |
| 85 | + | parserResult := FParser.Evaluate; // or: FParser.EvaluateExpression(parserResult); | |
| 86 | + | ``` | |
| 87 | + | ||
| 88 | + | 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: | |
| 89 | + | ||
| 90 | + | ``` pascal | |
| 91 | + | type | |
| 92 | + | TResultType = (rtBoolean, rtInteger, rtFloat, tDateTime, rtString); | |
| 93 | + | ||
| 94 | + | TFPExpressionResult = record | |
| 95 | + | ResString : String; | |
| 96 | + | Case ResultType : TResultType of | |
| 97 | + | rtBoolean : (ResBoolean : Boolean); | |
| 98 | + | rtInteger : (ResInteger : Int64); | |
| 99 | + | rtFloat : (ResFloat : TExprFloat); | |
| 100 | + | rtDateTime : (ResDateTime : TDatetime); | |
| 101 | + | rtString : (); | |
| 102 | + | end; | |
| 103 | + | ``` | |
| 104 | + | ||
| 105 | + | Jäsen `ResultType` -signaalit, jonka vain yksi tietokentistä on voimassa. On tärkeää ymmärtää tämä, koska parseri on hyvin tiukka [[Data type|tietotyyppien]] suhteen. | |
| 106 | + | ||
| 107 | + | Esimerkissä lisätään kaksi [[Integer/fi|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 [[Record/fi|tietueen]] parametriksi ja valitsee oikean komponentin, jos reaaliluku tulosta odotetaan: | |
| 108 | + | ||
| 109 | + | ``` pascal | |
| 110 | + | var | |
| 111 | + | parserResult: TFPExpressionResult; | |
| 112 | + | resultValue: Double; | |
| 113 | + | ... | |
| 114 | + | parserResult := FParser.Evaluate; // or: FParser.EvaluateExpression(parserResult); | |
| 115 | + | resultValue := ArgToFloat(parserResult); | |
| 116 | + | ``` | |
| 117 | + | ||
| 118 | + | > **Note:** Reaaliluvuilla on oltava [[period/fi|piste]] desimaalierottimena, ei [[Comma/fi|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. | |
| 119 | + | ||
| 120 | + | ### Lauseke muuttujalla | |
| 121 | + | ||
| 122 | + | Tässä esimerkissä lasketaan `sin(x)*cos(2*x)` jossa `x = 0.5` (0,5). | |
| 123 | + | ||
| 124 | + | #### Muuttujien määrittäminen | |
| 125 | + | ||
| 126 | + | Aluksi on määriteltävä muuttujat. On vain yksi `x`. Parserilla on [[Method|metodi]] `AddFloatVariable`, joka ilmoittaa reaaliluvun; on myös menetelmiä: | |
| 127 | + | ||
| 128 | + | - `AddBooleanVariable` | |
| 129 | + | - `AddStringVariable` | |
| 130 | + | - `AddDateTimeVariable` | |
| 131 | + | ||
| 132 | + | boolean-, merkkijono- ja päivämäärä/aika-muuttujille, vastaavasti. | |
| 133 | + | ||
| 134 | + | Jokainen näistä aliohjelmista odottaa muuttujan nimeä sekä sen oletusarvoa. Näyte funktiolle `sin(x)*cos(2*x)` kutsutaan vain: | |
| 135 | + | ||
| 136 | + | ``` pascal | |
| 137 | + | FParser.Identifiers.AddFloatVariable('x', 0.5); | |
| 138 | + | ``` | |
| 139 | + | ||
| 140 | + | `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`. | |
| 141 | + | ||
| 142 | + | Voit tietysti lisätä muita nimiä, esim. [[Constant/fi|vakiota]], kuten `e` jne. ([[Pi/fi|Pii (pi)]] on jo sisäänrakennettu). | |
| 143 | + | ||
| 144 | + | #### Lausekkeen määrittäminen | |
| 145 | + | ||
| 146 | + | Seuraavassa vaiheessa lausekkeen merkkijono lähetetään parserille: | |
| 147 | + | ||
| 148 | + | ``` pascal | |
| 149 | + | FParser.Expression := 'sin(x)*cos(2*x)'; | |
| 150 | + | ``` | |
| 151 | + | ||
| 152 | + | On välttämätöntä kutsua tätä muuttujien asettamisen jälkeen, koska jäsentimen on tiedettävä muuttuja lausekkeen analysoimiseksi. | |
| 153 | + | ||
| 154 | + | #### Lausekkeen laskeminen | |
| 155 | + | ||
| 156 | + | Tämä tehdään kuten aikaisemmin vakio lausekkeella - tässä on täydellinen aliohjelma, joka näyttää yhtälön ja sen tuloksen [[Dialog Examples|viestiruudussa (ShowMessage)]]: | |
| 157 | + | ||
| 158 | + | ``` pascal | |
| 159 | + | var | |
| 160 | + | FParser: TFPExpressionParser; | |
| 161 | + | resultValue: Double; | |
| 162 | + | begin | |
| 163 | + | FParser := TFPExpressionParser.Create(nil); | |
| 164 | + | try | |
| 165 | + | FParser.BuiltIns := [bcMath]; | |
| 166 | + | FParser.Identifiers.AddFloatVariable('x', 0.5); | |
| 167 | + | FParser.Expression := 'sin(x)*cos(2*x)'; | |
| 168 | + | resultValue := FParser.Evaluate.ResFloat; // or: resultValue := ArgToFloat(FParser.Evaluate); | |
| 169 | + | ShowMessage(FParser.Expression + ' = ' + FloatToStr(resultValue)); | |
| 170 | + | finally | |
| 171 | + | FParser.Free; | |
| 172 | + | end; | |
| 173 | + | end; | |
| 174 | + | ``` | |
| 175 | + | ||
| 176 | + | #### Muuttujien muuttaminen | |
| 177 | + | ||
| 178 | + | 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)"`. | |
| 179 | + | ||
| 180 | + | Jotta tämä käyttäytyisi enemmän kuin "muuttuja", lasketaan nyt testi funktion `x`-arvoja välillä -10 ja 10. | |
| 181 | + | ||
| 182 | + | 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: | |
| 183 | + | ||
| 184 | + | - Käytetään `AddFloatVariable` funktion palautusarvoa. | |
| 185 | + | - Haetaan tunniste kutsumalla `FindIdentifierByName`-tunnus muuttujan nimellä parametrina. | |
| 186 | + | - Haetaan tunnisteesta`Identifiers` jäsentimen tunnisteiden kokoelmasta käyttämällä muuttujan tunnettua indeksiä: On lisätty `x` ainoana muuttujana, joten sen on oltava indeksissä 0. | |
| 187 | + | ||
| 188 | + | Kun `Identifier` on tunnettu, muuttujan arvoa voidaan muuttaa avaamalla ominaisuus `AsFloat` (tai `AsDateTime` jne. Vastaavasti): | |
| 189 | + | ||
| 190 | + | ``` pascal | |
| 191 | + | var | |
| 192 | + | FParser: TFPExpressionParser; | |
| 193 | + | argument: TFPExprIdentifierDef; | |
| 194 | + | s: string; | |
| 195 | + | x: integer; | |
| 196 | + | f: double; | |
| 197 | + | begin | |
| 198 | + | s:=''; | |
| 199 | + | FParser := TFPExpressionParser.Create(nil); | |
| 200 | + | try | |
| 201 | + | // Enable the use of mathematical expressions | |
| 202 | + | FParser.BuiltIns := [bcMath]; | |
| 203 | + | ||
| 204 | + | // Add the function argument | |
| 205 | + | argument := FParser.Identifiers.AddFloatVariable('x', 0.0); | |
| 206 | + | ||
| 207 | + | // Define the function, using the argument name x as defined above | |
| 208 | + | FParser.Expression := 'sin(x)*cos(2*x)'; | |
| 209 | + | ||
| 210 | + | // Calculate some function values | |
| 211 | + | for x := -10 to 10 do | |
| 212 | + | begin | |
| 213 | + | argument.AsFloat := x; // Set the function argument value | |
| 214 | + | f := FParser.Evaluate.ResFloat; // Calculate the function value | |
| 215 | + | s := s + format('x[%d]:[%g]' + LineEnding, [x,f]); // Demo output to display the result | |
| 216 | + | end; | |
| 217 | + | ||
| 218 | + | // Show the result | |
| 219 | + | ShowMessage(s); | |
| 220 | + | ||
| 221 | + | finally | |
| 222 | + | FParser.Free; | |
| 223 | + | end; | |
| 224 | + | end; | |
| 225 | + | ``` | |
| 226 | + | ||
| 227 | + | ## Käyttäjän määrittämien funktioiden lisääminen | |
| 228 | + | ||
| 229 | + | 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. | |
| 230 | + | ||
| 231 | + | ``` pascal | |
| 232 | + | FParser.Identifiers.AddFunction('tan', 'F', 'F', @ExprTan); | |
| 233 | + | ``` | |
| 234 | + | ||
| 235 | + | 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: | |
| 236 | + | ||
| 237 | + | ``` pascal | |
| 238 | + | procedure ExprTan(var Result: TFPExpressionResult; Const Args: TExprParameterArray); | |
| 239 | + | var | |
| 240 | + | x: Double; | |
| 241 | + | begin | |
| 242 | + | x := ArgToFloat(Args[0]); | |
| 243 | + | Result.resFloat := tan(x); | |
| 244 | + | end; | |
| 245 | + | ``` | |
| 246 | + | ||
| 247 | + | 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 TFPExpression*Results* on ehkä hieman harhaanjohtava, koska tämä taulukko sisältää kaikki * tulo * -parametrit, jotka on määritelty AddFunction-metodin tulotyypeillä. | |
| 248 | + | ||
| 249 | + | --- | |
| 250 | + | *From the Free Pascal wiki, page "How To Use TFPExpressionParser/fi", as it stood on 2022-08-06 after a change by Kai Burghardt, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
