Kifejezések

(toc)


Reguláris kifejezések





     
A reguláris vagy szabályos kifejezések bizonyos szabályokat követő
karaktersorozatok. Arra szolgálnak, hogy megkönnyítsék a szöveggel való munkát,
mint a keresés, helyettesítés, ellenőrzés, stb. Az ilyen kifejezések sok programozási
nyelvbe be vannak építve, főleg a szövegek feldolgozására specializálódott
nyelvek, mint az XML vagy a HTML. A C++11 is szabványosította ezeket a
kifejezéseket a <regex> könyvtár révén. A reguláris kifejezések szabályai
eltérőek lehetnek, ezért több szabályzat 
(szintaxis) sorolható fel. Ezek közül a C++11 az ECMAScript, az alap és
bővített POSIX, az awk, a grep és az egrep szabálygyűjteményeit támogatja. A
következő példákban az ECMAScript szintaxisát fogjuk használni.





     
A reguláris kifejezéseket fokozatosan kell felépíteni, akár egy
programot. Először az alap dolgok működjenek, aztán jöhetnek a különböző
megszorítások:




  1. Legyen
    egy program, ami számokat fogad el bemenetnek. Az ECMAScript a számokat a
    [:digit:] vagy [:d:] szintaxissal azonosítja: regex r("[[:digit:]]");

  2. A
    fenti kifejezés csak egyjegyű számokat fogad el. Több számjegy esetén a végére
    kell írni egy + jelt: regex r("[[:digit:]]+");

  3. Ha
    tesztelni szeretnénk, hogy a beolvasott szám nem-e negatív, akkor a kifejezés
    elejébe a –? kombinációt kell tenni: regex r("-?[[:digit:]]+");

  4. Ha a
    felhasználó netán a + karaktert is használja a számok előtt, akkor erre is fel
    kell készíteni a fordítót, ugyanis ez egy speciális karakter az ECMAScript
    szintaxisában. Hogy figyelmen kívül hagyja ezt a karaktert, a \\+? jelt kell a
    kifejezés elejébe írni: regex r("(\\+?[[:digit:]]+");

  5. A
    kifejezés előtt lévő ellenőrzéseke kombinálni kell egy „vagy” kapuval, mert
    vagy az egyik, vagy a másik fordulhat elő: regex r("(\\+|-)?[[:digit:]]+");





Ezt a felépített kifejezést alkalmazzuk a
következő programban. Ha a bevitt számjegy nem felel meg a reguláris kifejezés
szabályainak, akkor a „Nem helyes!” üzenet jelenik meg. A program a q billentyű
beviteléig fut.





#include <iostream>


#include <regex>


#include <string>





using namespace std;





int main()


{


      string input;


      regex r("(\\+|-)?[[:digit:]]+");





      while(true) //végtelen ciklus


      {


            cout
<< "Irj be egy egesz szamot: ";


            cin >>
input;


            if(input=="q")
break; // q esetén
kilép


            if(regex_match(input,r))


                  cout
<< "Helyes!" << endl;


            else


                  cout
<< "Nem helyes!" <<
endl;


      }


}





A reguláris kifejezés helyességét az std::regex_match() függvény ellenőrzi. A kimenet:





Irj be egy egesz
szamot: 23


Helyes!


Irj be egy egesz
szamot: +51


Helyes!


Irj be egy egesz
szamot: -79


Helyes!


Irj be egy egesz
szamot: 6.7


Nem helyes!


Irj be egy egesz
szamot: +-41


Nem helyes!


Irj be egy egesz
szamot: abc


Nem helyes!


Irj be egy egesz
szamot: q





Ha valós számokra is hasonlóképp kell
felépíteni a reguláris kifejezést. Ott a tizedespont utáni rész opcionális,
ezért a ([[:digit:]]+)? kifejezéssel jelöljük. Ugyanígy a tizedespont is
opcionális ha nincs utána semmi, így az egész második részt (\\.)? közé lehet
tenni:
regex rr("((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?");





Ha a feladat, hogy tudományos formátumú
számokat lehessen beírni, mint pl. -1.23e+06 vagy 1E3, akkor azzal bővítjük az
előző kifejezést, hogy az exponenciális tag is opcionális részese lehet a
számnak. Az exponenciális részt a ((e|E)((\\+|-)?)[[:digit:]]+)? kifejezés
jelenti:
regex rrr("((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?((e|E)((\\+|-)?)[[:digit:]]+)?");





Az alapértelmezettként használt ECMAScript
logikája hamar elsajátítható, de beállítható más szintaxis is, például a POSIX
grep:





#include <iostream>


#include <string>


#include <regex>





int main()


{


    std::string str = "zzxayyzz";


    std::regex re1(".*(xay)"); //
ECMAScript


    std::regex re2(".*(xay)", std::regex::grep); // POSIX grep





    std::cout << "Kereses 'xay' utan az 'zzxayyzz' szovegben:\n";


    std::smatch m;


   
std::regex_search(str, m, re1);


    std::cout << " ECMAScript: " << m[0] << '\n';


   
std::regex_search(str, m, re2);


    std::cout << "
POSIX grep: "
<< m[0] << '\n';


}





A fenti program egy keresést végez az str stringben. A keresést az std::regex_search() függvény végzi, melynek első paramétere egy string amiben a keresés
zajlik, a második egy konténer, ami a találatokat tartalmazza, a harmadik pedig
a keresés reguláris kifejezése. Az eredmény:





Kereses 'xayy'
utan az 'zzxayyzz' szovegben:


 ECMAScript: zzxay


 POSIX grep: zzxay





Raw stringek





A raw stringek olyan stringek, melyekben a
feloldójelek (pl. \n, \t, \) nem érvényesek. Szintaxisa: R”(szoveg)”. A
következő program jól szemlélteti a hagyományos és a raw string közti
különbséget:








#include <iostream>


#include <string>





using namespace std;





int main()


{


    string normal_str="Elso sor.\nMasodik sor.\nUzenet vege.\n";


    string raw_str=R"(Elso sor.\nMasodik sor.\nUzenet vege.\n)";


   
cout<<normal_str<<endl;


   
cout<<raw_str<<endl;


    return(0);


}





A kimenet:





Elso sor.


Masodik sor.


Uzenet vege.





Elso
sor.\nMasodik sor.\nUzenet vege.\n





A raw string egyik célja, hogy valamennyire
egyszerűsítse a reguláris kifejezések használatát. Ezzel közvetlen bevihetők az
ECMAScript kifejezések anélkül, hogy a feloldójeleket a fordító felismerné. Egy
korábbi példában a bemenő számok helyességét a következőképp ellenőriztük:





regex r("(\\+|-)?[[:digit:]]+");





Ugyanez raw stringgel:





regex integer(R"((\+|-)?[[:digit:]]+)");





Lambda függvények és
kifejezések





A reguláris kifejezések és a raw stringek
mellett egy másik hasznos nyelvezet a lambda. A lambda kifejezések lehetővé
teszik, hogy a név nélkül definiáljunk és használjunk függvényeket. Használható
a függvényobjektumok helyett, így nem kell külön osztályt és tagfüggvényt
definiálni. A következő lambda kifejezés egy olyan függvényt definiál, amelyik
egy számot térít vissza:





[]() -> int
{ return 4; }();





A fenti függvény igazából semmi hasznosat
nem tesz, csupán visszatéríti a 4-et, ami például kiírásra használható:





int result = []()
-> int { return
4; }();


cout << result << endl;





Kicsit hasznosabb a következő:





int result = [](int input) -> int
{ return 2 * input; }(10);


cout << result << endl;





A fenti kifejezésnek van agy bemenő paramétere
(input) és visszatéríti ennek dupláját. A függvény rögtön a deklarálás után
megkapja a 10 paramétert, így a kiírt érték 20. Hasonlóképp felírható két szám
összege:





int result = [](int a,int b) -> int { return a + b;
}(2,4);


cout << result << endl;





Akár a reguláris kifejezések, a lambda
kifejezések is kombinálhatók, egymásba építhetők:





int result = [](int m, int n) -> int { return m + n; }
([](int a) -> int
{ return a; }(2),[](int
a) -> int { return
a; }(3));


cout << result << endl;





A fenti lambda függvényben két másik lambda
függvény szerepel, egyik 2-t, másik 3-at térít vissza, a fő függvény pedig
összeadja ezeket, mint bemenő paramétereket. A függvénymutatók működnek a
lambda függvényekre is:





auto func = [](int a, int b) -> int { return a+b; };


cout << func(2, 3) << endl;





Az auto típus a func változót a lambda
kifejezésre mutató mutatóként fogja deklarálni. Tegyük fel, hogy egész
számokkal megpakolt vektor tartalmát kell rendezni a sort() algoritmussal. Ez a
hagyományos C++98 fordítóval a következőképp néz ki:





#include <iostream>


#include <vector>


#include <algorithm>





bool compare(int i,int j)


{


      return (i<j);


}





using namespace std;





int main()


{


      vector<int> v {3,1,7,4,8,9,5,6,2,10};


      for(int i = 0; i <
v.size(); i++) cout << v[i] << "
"
; cout << endl;


      sort(v.begin(),
v.end(), compare); //rendezés


      for(int i = 0; i <
v.size(); i++) cout << v[i] << "
"
; cout << endl;





      return 0;


}





Lambda kifejezéssel ugyanez elérhető,
megspórolva a compare() függvény deklarálását:





sort(v.begin(), v.end(), [](int i, int j) -> bool{ return (i <
j);}); //rendezés








Függvénymutatók





A függvénymutatók is a programozás
egyszerűsítésére szolgálnak. Egy függvény nem más, mint egy memóriacím, ahonnan
az utasítások kezdődnek. A függvény hívásához elegendő ezt a címet ismerni.





A következő függvénymutató olyan
függvényre mutat, melynek paramétere két
char, visszatérített típusa pedig int. A mutató
neve
func, amit bármilyen
függvényhez lehet használni, melynek ilyen a felépítése.





      int (*func)(char,char) = NULL;





A függvénymutatók az osztályok
tagfüggvényeire is mutathatnak.





      int (MyClass::*Classfunc)(char,char) = NULL;





Mindkét mutató NULL mutatónak van
inicializálva, de lehet rögtön a függvény címét is adni (pl.
&fuggveny). Ha a mutató megkapta a függvény címét,
a függvény rajta keresztül hívható:





int result = func(10,2);


int result =
(obj.*Classfunc)(10,2);





Mivel mutatóról van szó, ez paraméterként
is átadható egy másik függvénynek.





void PassPtr(int (*funcptr)(char, char))


{


   int result =
(*funcptr)(20,3);     // call using function pointer


   cout << "PassPtr:
"
<< result << endl;


}





A függvényt a következőképp lehet
meghívni:
PassPtr(func);





A függvénymutató egy függvény
visszatérített értéke is lehet:





int
(*ReturnPtr(char muvelet))(char, char)


{


   if(muvelet
== '+')


      return
&Plus;


   else


      return
&Minus;


}





Ebben az esetben a Plus() és Minus() két hasonló függvény. A következő példában
összesítve vannak az eddig felsorolt példák:





#include <iostream>


using namespace std;





class MyClass


{


      public:


            int TagOsszeg(char a, char b) //tagfüggvény


            {


                  return a+b;


            }


            int TagKulonbseg(char a, char b) //tagfüggvény


            {


                  return a-b;


            }


};





int Osszeg (char a, char b) //sima függvény


{


      return a+b;


}





int Kulonbseg
(char a, char
b) //sima függvény


{


      return a-b;


}





void PassPtr(int (*funcptr)(char, char))


{


   int result =
(*funcptr)(20,3);


   cout << "PassPtr(20,3):
"
<< result << endl;


}





int
(*ReturnPtr(char muvelet))(char, char)


{


   if(muvelet
== '+')


      return
&Osszeg;


   else


      return
&Kulonbseg;


}








int main()


{


      int (*func)(char, char) = NULL; //függvénymutató egy sima függvényre


      int (MyClass::*Classfunc)(char, char) = NULL; //függvénymutató a tagfüggvényre


      int result;


     


      func = &Osszeg;


      result = func(12,3);


      cout << "Osszeg(12,3):
"
<< result << endl;


     


      func = &Kulonbseg;


      result = (*func)(12,3);


      cout << "Kulonbseg(12,3):
"
<< result << endl;


     


      MyClass obj1;


      Classfunc = &MyClass::TagOsszeg;


      result = (obj1.*Classfunc)(10,2);


      cout << "TagOsszeg(10,2):
"
<< result << endl;


     


      MyClass *obj2 = new MyClass;


      Classfunc = &MyClass::TagKulonbseg;


      result = (obj2->*Classfunc)(10,2);


      cout << "TagKulonbseg(10,2):
"
<< result << endl;


     


      PassPtr(func);


     


      result = (*ReturnPtr('+'))(3,7);


      cout << "(*ReturnPtr('+'))(3,7):
"
<< result << endl;


     


     


      return 0;


}





A kimenet:





Osszeg(12,3): 15


Kulonbseg(12,3):
9


TagOsszeg(10,2):
12


TagKulonbseg(10,2):
8


PassPtr(20,3):
17


(*ReturnPtr('+'))(3,7):
10





A függvénymutatók legnagyobb hasznát
igazából a tömbben való alkalmazáskor lehet érezni. Ilyenkor ugyanis a
függvényeket indexelni lehet.





#include <iostream>


using namespace std;





int Osszeg (char a, char b)


{


      return a+b;


}





int Kulonbseg
(char a, char
b)


{


      return a-b;


}





int Szorzat (char a, char b)


{


      return a*b;


}





int Hatvany (char a, char b)


{


      int h=1;


      for(int
i=1; i<=b; i++)


            h=h*a;


      return h;


}





int main()


{


   int
(*tomb[4])(char, char)
= {NULL}; //10 darab függvénymutató





   tomb[0]=&Osszeg;


   tomb[1]=&Kulonbseg;


   tomb[2]=&Szorzat;


   tomb[3]=&Hatvany;





   for(int i=0; i<4; i++)


            cout
<< tomb[i](6,2) << endl;


  


   return 0;


}





A kimenet:





8


4


12


36






Find Out
Related Post



Ikuti AltairGate.com pada Aplikasi GOOGLE NEWS : FOLLOW (Dapatkan Berita Terupdate tentang Dunia Pendidikan dan Hiburan). Klik tanda  (bintang) pada aplikasi GOOGLE NEWS.

Top Post Ad

Below Post Ad

s