Oktatási segédlet 2014
A kutatás a TÁMOP 4.2.4.A/2-11-1-20120001 azonosító számú Nemzeti Kiválóság Program – Hazai hallgatói, illetve kutatói személyi támogatást biztosító rendszer kidolgozása és működtetése országos program című kiemelt projekt keretében zajlott. A projekt az Európai Unió támogatásával, az Európai Szociális Alap társfinanszírozásával valósul meg.
Szekvencia, szelekció, iteráció If Switch/case for, while do while foreach
Szekvencia, szelekció, rekurzió if switch Rekurzív függvények Tail rekurzív függvények (vég rekurzió)
Az imperatív és OO nyelvek a szekvencia elvén működnek Minden programsor végrehajtódik egymás után, ahogy le vannak írva a forrásszövegben Az OO nyelvek osztályai sem képeznek kivételt, a főprogramban (main függvény, stb) a szekvencia elve érvényesül Szükség van bizonyos esetekben a forward deklarációra Az osztályokban a deklaráció sorrendje nem befolyásolja a végrehajthatóságot, és a sorrendet
A funkcionális nyelvekben a szekvencia elve a függvényeken belül érvényes Hivatkozási helyfüggetlenség elve érvényesül A futtatáshoz gráf átíró, vagy gráf újraíró rendszereket használnak GRS, FGRS teszi lehetővé a sajátos kiértékelést A magasabb rendű függvények alkalmazhatóak az ilyen rendszerekben
Az imperatív és OO nyelvekben a for, while és a do while ciklusokat alkalmazzuk az iteráció megvalósítására
for(i = 0; i< 10; i++) { … }
while (i < 10) { …, i++;}
A funkcionális nyelvekben a ciklusokat nem használhatjuk Helyettük a rekurzió két formáját alkalmazzuk Az első a hagyományos rekurzió, amely a bázis feltétel hatására megáll. A másik változat a tail-rekurzív, vagy vég rekurzív ismétlés, amely nem áll meg a verem túlcsordulása miatt. Ezt a GRS, és az FGRS kiértékelő rendszer teszi lehetővé. Hiányzik a destruktív értékadás is: i = i + 1…
factorial(0) -> 1; factorial(N) -> N * factorial(N-1). A függvény önmagát hívja mindaddig, amíg a verem be nem telik, vagy el nem érjük a nulla értéket a kiértékelés során A rekurzió ezen formája nem, vagy csak korlátozott futásszám mellett tudja helyettesíteni az iterációt
A vég rekurzió két feltétele: ◦ Az első, hogy a rekurzív hívás az utolsó utasítás legyen a függvényben, vagy annak minden ágában, ◦ A másik feltétel, hogy a rekurzív hívás ne szerepeljen kifejezésben mint a N * fakt(N - 1)
A vég rekurzió alkalmas az iteráció kiváltására Nem használja a vermet, így az nem telik be…
Erlang: fact(N) -> factr(N, 1). factr(0, X) -> X; factr(N, X) -> factr(N-1, N*X). Clean: fact n = factr n 1 factr 0 x = x factr n x = factr (n-1) (n*x)
F#: let rec fakt n = match n with ◦ | 0 -> 1 ◦ | n -> n * fakt(n-1)
let rec fakt n = if n=0 then 1 else n * fakt (n-1)
A foreach a listák és a tömbök bejárására alkalmas A foreach-ben a lista read-only tulajdonságú A foreach automatikus, és a típushelyességre is „ügyel” foreach (int x in lista) ◦ { X; //feldolgozása
◦ }
A funkcionális nyelvekben halmaz kifejezéseket használunk A halmaz kifejezés a matematikai leírásra hasonlít Nem a halmaz elemeit, hanem a halmazba tartozás feltételét írjuk le segítségével Így elvben végtelen hosszú adatszerkezeteket is tudunk reprezentálni a programokban (csak elvben…)
A halmaz elemeit definiáljuk matematikai módszerekkel: {x | x eleme X, x < 10}
A fenti kifejezést leírhatjuk lista ifejezéssel pl. Erlangban: [X || X <- XH, X < 10] A kifejezés jelentése ugyanaz, mint a matematikai leírásnak, csak ez a változat futtatható számítógépen…
Megjegyzés: az X halmazbak mindkét esetben léteznie kell!
A foreach és a halmazkifejezés hasonlítanak egymásra Amennyiben a halmazkifejezést listák bejárására használjuk, a működésük megegyezik a foreach működésével A két szerkezet nem ekvivalens, de nagyon hasonlóak
Az imperatív nyelveknél a függvényeket modulokba rendezzük A funkcionális nyelvekben ugyanez a helyzet Az OOP nyelvekben a függvényeket osztályokba soroljuk A funkcionális nyelvek (egyes szélsőséges kivételektől eltekintve) nem tartalmazzák az osztály, vagy class konstrukciót.
A funkcionális nyelvek moduljai, a bennük szereplő lokális függvények és az exportált függvények a bonyolultság szempontjából nagy hasonlóságot mutatnak az osztályok metódusaival, és azok public, protected és private védelmi szintjeivel. class { private void y(int x){…} -module(a). –export([f/1]) …
Halmazkifejezések használata (Lista kifejezések) Lusta vagy mohó kifejezés kiértékelésének lehetősége Végrekurzió Változók egyszeri kötése Destruktív értékadás hiánya Ciklus típusú iterációk hiánya Számos esetben függvények hívási helyfüggetlensége Mintaillesztés Currying Magasabb rendű függvények (λ (lambda) kifejezések használata)
Az imperatív nyelveknél megszokott library modulokban, vagy az objektum orien- tált programok névtereibe rendezett funkcionalitás ugyanúgy típus szerint összegyűj- tött függvények (alprogramok, vagy metódusok) halmazát jelenti, mint az Erlang prog- ramok moduljaiba rendezett függvények esetében. Így az objektum orientált nyelvek osztályaiban a metódusokra alkalmazott mértékek némi átdolgozás után alkalmasak a funkcionális nyelvek függvényeinek a jellemzésére.
A funkcionális nyelvekben a függvényeket modulokba rendezzük. A függvények moduljait exportáljuk, hogy azok a modulon kívül is elérhetőek legyenek. Ez a módszer hasonlít az objektum orientált nyelvekben megismert osztályokra és láthatósági (visibility) szintjeire, mint a private, public, protected.
A funkcionális nyelvek némelyikében nyelvben a nem exportált függvények és az adatok lokálisak a modulra nézve, és a függvényekben definiált változók a függvényre. Ha globális adatokat szeretnénk definiálni, akkor rekordokat készítünk, amelyeket fejléc fájlokban, vagy adatbázisokban helyezünk el.
Ez a struktúra, vagyis a modul és a benne definiált függvények tehát hasonlóak az osztályhoz és metódusaihoz. A globális változók a publikus mezőkhöz és az export lista hatása az osztály publikus védelmi szintjeinek a hatásához. A sok egyezés ellenére a paradigmák különböznek, de a bonyolultság elemzéséhez fontos feltárni ezeket a hasonlóságokat
Összefoglalva, a nem funkcionális nyelvekre alkalmazott mértékek közül azok használhatóak funkcionális programok mérésére, amelyek az alprogramok tulajdonságait mérik, vagy az azok törzsében található vezérlő szerkezeteket és adatokat, esetleg az alprogramok közti kapcsolatokat tárják fel, mint a függvény hívás, vagy az adatfolyam.
Hasonlítsuk össze a vezérlő szerkezeteket a McCabe bonyolultság alapján! Keressünk olyan bonyolultági mértékeket, amelyeket egyaránt alkalmazhatunk imperatív, OOP, és funkcionális nyelvek mérésére is! Készítsünk egyszerű OOP osztályokat, és számoljuk ki az McCabe ciklomatikus számukat! Végezzük el ugyanezt funkcionális programokon is!
Készítsünk olyan Erlang modult, amely tartalmaz szekvenciát, szelekciót, és rkurzv függvényhívásokat! Mérjük meg a modul McCabe számát! Mérjük meg a beágyazottsági mértékek maximumát a modulra! Mérjük meg ugyanezt a függvényekre egyenként!
Hogyan tudnánk csökkenteni az McCabe számot, ha a modulban mélyen beágyazott case, vagy if kifejezéseket írtunk? Mérjük meg a beágyazott kifejezéseket jellemző ciklomatikus számot! Emeljünk ki legalább kettő mélységben beágyazott case kifejezéseket! Hogyan befolyásolja a kiemelés a ciklomatikus számot?
Hogyan befolyásolja a ciklomatikus számot kivételkezelő rutinok alkalmazása? Mi a bonyolultabb: egy 12-es McCabe számmal rendelkező függvény, vagy 12 db 1es ciklomatikus számmal rendelkező függvény? Melyik a bonyolultabb: egy 10 mélységben beágyazott case kifejezés, vagy 10 db egy mélységben beágyazott?
optimize extract_fun (expr_type, case_expr) where max_depth_of_cases > 3 ... optimize extract_fun where max_depth_of_cases > 3
optimiz extract_function where max_depth_of_cases > 1 and number_of_function < 10 limit 2
f({A, B})-> case A of send -> case B of {Pid, Data} -> case Pid of {pid, P} -> P ! Data; _ -> Pid ! B end; _ -> null ! B end}; _ -> mod:wait() end.
f({A, B})-> case A of send -> f0(B); _ -> mod:wait() end. f0(B) -> case B of {Pid, Data} -> f1(B, Data, Pid); _ -> {null ! B} end. f1(B, Data, Pid) -> case Pid of {pid, P} -> P ! Data; _ -> Pid ! B end.
optimize extract_function (exprtype, case_expr) where f_mcCabe > 6 and f_max_depth_of_structures > 2 and f_max_depth_of_cases > 1 limit 7;
optimize move_fun (targetmod, storage) where f_name like ’opt’ limit 1;
optimize extract_fun where max_depth_of_cases > 1 and number_of_function < 10 limit 2;