Az ERLANG programozási nyelv

Szabványos könyvtárak

A nyelv számos beépített könyvtárat tartalmaz, melyek a következők:

Egysoros leírások

Ez a rész egysoros leírásokat tartalmaz az Erlang kernel és stdlib függvény-könyvtáraiból, abból a célból, hogy ezt olvasgatva egyszerűen és gyorsan eldönthető legyen, hogy melyik függvényt kell jobban megnézni.

„application” modul

Általános OTP alkalmazás funkciók

Modul:config_change(Valtozott, Uj, Torolt) -> ok 

      Az alkalmazás konfigurációs beállításait lehet vele frissíteni.

Modul:prep_stop(Allapot) -> UjAllapot

      Felkészíti az alkalmazást a leállásra.

Modul:start(StartTipus, StartParam) -> {ok, Pid} | {ok, Pid, Allapot} | {error, Kivalto}

      Elindítja az alkalmazást.

Modul:start_phase(Fazis, StartTipus, FazisParam) -> {ok} | {error, Kivalto}

      Az alkalmazás kiterjesztett indítása.

Modul:stop(Allapot)

      Alkalmazás leállítása utáni műveletek (pl. fájlok bezárása, stb.).

get_all_env(Alkalmazas) -> Kornyezet

      Visszaadja az alkalmazás konfigurációs beállításait.

get_all_key(Alkalmazas) -> {ok, Kulcsok} | undefined

      Visszaadja az alkalmazást leíró kulcsokat.

get_application(Pid | Modul) -> {ok, Alkalmazas} | undefined

      Visszaadja a folyamatot vagy modult tartalmazó alkalmazást.

get_env(Alkalmazas, Par) -> {ok, Ertek} | undefined

      Visszaadja egy konfigurációs beállítás paraméterének értékét.

get_key(Alkalmazas, Kulcs) -> {ok, Ertek} | undefined

      Visszaadja egy alkalmazást leíró kulcs értékét.

load(AlkLeiras, Elosztott) -> ok | {error, Kivalto}

      Betölt egy alkalmazást.

loaded_application() -> ({Alkalmazas, Leiras, Vsn})

      Visszaadja a már betöltött alkalmazásokat.

permit(Alkalmazas, Bool) -> ok | {error, Kivalto}

      Megváltoztatja az alkalmazás jogosultságát egy adott node-on való futásra.

set_env(Alkalmazas, Par, Ertek, Timeout) -> ok

      Beállítja egy konfigurációs paraméter értékét.

start(Alkalmazas, Tipus) -> ok | {error, Kivalto}

      Betölt és elindít egy alkalmazást.

start_type() -> StartTipus | local | undefined

      Visszaadja a folyamatban lévő alkalmazás-indulás típusát.

stop(Alkalmazas) -> ok | {error, Kivalto}

      Megállít egy alkalmazást.

takeover(Alkalmazas, Tipus) -> ok | {error, Kivalto}

      Átvesz egy elosztott alkalmazást.

unload(Alkalmazas) -> ok | {error, Kivalto}

      Kitöröl egy betöltött alkalmazást.

unset_env(Alkalmazas, Par, Timeout) -> ok

      Kitörli egy konfigurációs paraméter értékét.

which_applications(Timeout) -> ({Alkalmazas, Leiras, Vsn})

      Visszaadja az éppen futó alkalmazásokat.

„base64” modul

Base 64 kódolást és dekódolást valósít meg. Több információt erről itt találhatsz.

encode_to_string(Adat) -> Base64String

      Kódolja az adatokat base 64 formátumba.

mime_decode_string(Base64String) -> Adat

      Dekódolja a base 64 stringet adatokká.

„beam_lib” modul

BEAM fájl formátumot kezelő modul.

chunks(Beam, (DarabRef)) -> {ok, {Modul, (DarabAdat)}} | {error, beam_lib, Kivalto}

      Beolvassa a kiválasztott darabokat (chunk) egy BEAM fileból vagy binárisból.

chunks(Beam, (DarabRef), (Opcio)) -> {ok, {Modul, (DarabAdat)}} | {error, beam_lib, Kivalto}

      Beolvassa a kiválasztott darabokat (chunk) egy BEAM fileból vagy binárisból.

clear_crypto_key_fun() -> {ok, Eredmeny}

      Kitörli a jelenlegi titkosítási kulcs függvényt.

cmp(Beam1, Beam2) -> ok | {error, beam_lib, Kivalto}

      Összehasonlít két BEAM fájlt.

cmp_dirs(Kvtar1, Kvtar2) -> {Csak1, Csak2, Kulonbozo} | {error, beam_lib, Kivalto}

      Összehasonlítja két külön könyvtárban lévő BEAM fájlokat.

crypto_key_fun(TitkosKulcsFgv) -> ok | {error, Kivalto}

      Beállítja a jelenlegi titkosítási kulcs függvényt.

diff_dirs(Kvtar1, Kvtar2) -> ok | {error, beam_lib, Kivalto}

      Összehasonlítja két külön könyvtárban lévő BEAM fájlokat.

format_error(Kivalto) -> String

      Visszaad egy bővebb leírást a BEAM olvási hibáról.

info(Beam) -> ({item, Info}) | {error, beam_lib, Kivalto}

      Információkat szolgáltat a BEAM fájlról.

md5(Beam) -> {ok, {Modul, Md5}} | {error, beam_lib, Kivalto}

      Visszaadja a BEAM fájlban lévő modul verzióját.

strip(Beam1) -> {ok, {Modul, Beam2}} | {error, beam_lib, Kivalto}

      Kitöröl olyan darabokat a BEAM fájlból, amire a betöltőnek nincs szüksége.

strip_files(Fileok) -> {ok, ({Modul, Beam2})} | {error, beam_lib, Kivalto}

      Kitöröl olyan darabokat a BEAM fájlból, amire a betöltőnek nincs szüksége.

strip_release(Kvtar) -> {ok, ({Modul, Filenev})} | {error, beam_lib, Kivalto}

      Kitöröl olyan darabokat BEAM fájlokból, amire a betöltőnek nincs szüksége.

version(Beam) -> {ok, {Modul, (Verzio)}} | {error, beam_lib, Kivalto}

      Visszaadja a BEAM fájlban lévő modul verzióját.

„c” modul

Erlang shell parancssori függvényeit tartalmazza.

bt(Pid) -> void()

      Egy folyamat (Erlang process) vermének back-trace-ét mutatja.

c(Fajl, Opciok) -> {ok, Modul} | error

      Lefordít és betölt egy forráskódot tartalmazó fájlt.

cd(Kvtar) -> void()

      Megváltoztatja a jelenlegi könyvtárat.

flush() -> void()

      Minden shellnek küldött üzenetet kiír a képernyőre a bufferból.

help -> void()

      Alapvető súgót jelenít meg.

i(X, Y, Z) -> void()

      Információkat ad a <X.Y.Z> pid-jű folyamatról.

l(Modul) -> void()

      Betölt vagy újratölt egy modult.

lc(Fajlok) -> ok

      Lefordít mindent fájlt egy listában.

ls() -> void()

      Kiírja a jelenlegi könyvtárban lévő fájlokat.

ls(Kvtar) -> void()

      Kiírja egy könyvtárban lévő fájlokat.

m() -> void()

      Megmondja, mely modulok vannak jelenleg betöltve.

m(Modul) -> void()

      Infomációkat ad vissza egy modulról.

memory() -> ({Tipus, Meret})

      Memória foglaltsági információkat ír ki.

memory((Tipus) -> ({Tipus, Meret})

      Memória foglaltsági információkat ír ki.

nc(Fajl, Opciok) -> {ok, Module} | error

      Lefordít és minden node-on betölt egy fájlt.

ni() -> void()

      Információkat ír ki a rendszerről.

nl(Modul) -> void()

      Betölt egy modult minden node-on.

nregs() -> void()

      Bejegyzett folyamatokról információkat ír ki.

pid(X, Y, Z) -> pid()

      X, Y, Z-t átalakítja pid-dé.

pwd() -> void()

      Kiírja a jelenlegi könyvtár nevét.

q() -> void()

      Kilépés. Az init:stop()-ot hívja meg.

xm(ModSpec) -> void()

      Kereszthivatkozás ellenőrzést végez az adott modulon.

y(Fajl) -> YeccRet

      LALR-1-es parser-t generál.

y(Fajl, Opciok) -> YeccRet

      LALR-1-es parser-t generál.

„calendar” modul

Helyi és univerzális időt, hét napjait kezelő, dátumkonverziókat megvalósító modul.

date_to_gregorian_days(Ev, Ho, Nap) -> Napok

      Kiszámítja a 0. évtől a megadott dátumig eltelt napok számát.

datetime_to_gregorian_seconds({Datum, Ido}) -> Masodperc

      Kiszámítja a 0. évtől a megadott pillanatig eltelt másodpercek számát.

day_of_the_week(Ev, Ho, Nap) -> NapSzam

      Visszaadja, hogy a hét melyik napjára esik az adott dátum.

gregorian_days_to_date(Napok) -> Datum

      Visszaadja a dátumot a napok számából.

gregorian_seconds_to_datetime(Masodperc) -> {Datum, Ido}

      Visszaadja az időpontot az eltelt másodpercek alapján.

is_leap_year(Ev) -> bool()

      Visszaadja, hogy szökőév-e.

last_day_of_the_month(Ev, Ho) -> Nap

      A megadott év megadott hónapjának utolsó napját adja vissza.

local_time() -> {Datum, Ido}

      Kiszámítja a helyi időt.

local_time_to_universal_time_dst({Datum1, Ido1}) -> {Datum2, Ido2}

      Helyi időt univerzális idővé alakít át.

now_to_datetime(Most) -> {Datum, Ido}

      Átkonvertálja a mostot időpillanattá.

now_to_local_time(Most) -> {Datum, Ido}

      Átkonvertálja a mostot helyi idővé.

seconds_to_daystime(Masodperc) -> {Napok, Ido}

      A másodpercek számából kiszámítja az időt.

seconds_to_time(Masodperc) -> Time

      A másodpercek számából kiszámítja az időt.

time_to_seconds(Ido) -> Masodperc

      Kiszámítja az éjfél óta eltelt másodperceket egészen a megadott ideig.

universal_time() -> {Datum, Ido}

      Visszaadja a jelenlegi univerzális időt.

universal_time_to_local_time({Datum1, Ido1}) -> {Datum2, Ido2}

      Konvertálja az univerzális időt helyi idővé.

valid_date(Ev, Ho, Nap) -> bool()

      Dátumhelyességet ellenőriz.

Gráfkezelés

Az Erlang nyelvben a gráfkezelés az OTP könyvtár digraph modulja által támogatott, így külön, harmadik féltől származó könyvtárcsomag nélkül lehet könnyedén, az Erlang nyelv koncepciójához jól illeszkedő módon kezelni a gráfokat. A digraph modul az irányított, címkézett gráfok kezelésére alkalmas, a modul a nevét a directed graph rövidítéseként kapta. Irányítatlan gráfokat úgy tudunk létrehozni, hogy minden élt mindkét irányba felveszünk azonos címkével. A párhuzamos élek és a hurokélek megengedettek a digraph modulban.

Bevezetőként tekintsük át azokat a fontos gráfelméleti fogalmakat, amelyeket a digraph modul is használ, és a továbbiakban hivatkozni fogunk rájuk.

Egy modulbeli gráf egy (V, E) párként reprezentálható. A V a gráfcsúcsok véges halmaza, az E pedig az (irányított) élek véges halmaza. Az E a V × V (a V Descartes-szorzata önmagával) részhalmazaként áll elő. A V halmaz lehet üres is (ekkor szükségszerűen az E is üres lesz), az így előállt egyedi gráfot üres gráfnak nevezzük. Mind a csúcsok, mind az élek Erlang termek által kerülnek reprezentálásra, ezeket a digraph modult felhasználó programozó adhatja meg. A csúcsokat, illetve az éleket azonosító termeknek értelemszerűen egy gráfon belül egyedinek kell lenniük.

A gráfok csúcsaihoz, illetve éleihez egyaránt további információk is csatolhatóak, ezeket címkéknek nevezzük, az így előállt gráfot pedig címkézett (irányított) gráfnak hívjuk. A címkék szintén Erlang termekként kerülnek reprezentálásra a modulban. Súlyozott gráfot ezáltal úgy hozhatunk létre, ha címkéknek számokat használunk.

Egy e = (v, w) élre azt mondjuk hogy a v csúcsból indul (ered), és a w csúcsba érkezik. Egy gráfcsúcs kimenő fokszámának (kifokának) a csúcsból kiinduló, bemenő fokszámának (befokának) pedig a csúcsba beérkező élek számát nevezzük. Ha létezik egy v gráfcsúcsból induló és w csúcsba érkező él, akkor azt mondhatjuk, hogy a w csúcs a v kimenő szomszédja, illetve a v gráfcsúcs pedig a w bemenő szomszédja. A v1 és vk gráfcsúcsok közötti P sétának egy (V, E) gráfban a V-beli v1, v2, ..., vk csúcsok nem üres, véges sorozatát nevezzük, ahol minden (vi, vi+1) rendezett párra létezik él E-ben (1 <= i < k). A P séta hossza ekkor k - 1. A P sétát egyszerűnek nevezzük, ha az összes érintett csúcs különböző, legfeljebb a két végcsúcs lehet megegyező. A P séta zárt, ha v1 = vk, egyébként a séta nyitott. A P sétát útnak hívjuk, ha egyszerű és nyitott. Körnek akkor mondjuk P sétát, ha egyszerű és zárt. Azt a speciális kört, amelynek hossza 1, hurokélnek hívjuk. Aszerint, hogy egy gráf tartalmaz-e kört, megkülönböztetünk ciklikus és aciklikus gráfokat.

Gráf létrehozása

Új gráfot a new függvénnyel tudunk létrehozni, ami az üres gráffal tér vissza.

Példa:

create_empty_graph() -> digraph:new().

A gráf létrehozásakor egy paraméterrel megadhatjuk, hogy milyen típusú gráfot kívánunk létrehozni. A gráfunk egyrészt lehet ciklikus vagy aciklikus, az utóbbiban nem hozható létre kör. A gráf másrészt lehet védett vagy privát. A privát gráfhoz csak a kurrens processz férhet hozzá, a védett gráf ellenben a többi processz számára is olvasható. A gráf alapértelmezetten ciklikus és védett.

Példa:

create_empty_graph() -> digraph:new(acyclic | private).

A gráf típusára vonatkozó információk kinyerhetőek egy gráfból az info függvénnyel, amely kulcs-érték párok listájával tér vissza.

Gráfcsúcsok kezelése

A digraph modul függvényei által lehetőség van csúcsok felvételére, módosítására és törlésére, illetve a csúcsoknak és címkéinek, kifokának, befokának, valamint még kimenő és bemenő szomszédjainak is a lekérdezésére.

Gráfcsúcs felvétele

Egy meglévő gráf csúcsai közé újat felvenni, illetőleg a meglévőket módosítani egyaránt az add_vertex függvénnyel lehetséges. A függvény első paraméterének a gráfot, a másodiknak a csúcsot, a harmadiknak pedig a címkéjét kell megadni. A függvény visszatérési értéke a létrehozott (vagy módosított) csúcs.

Példa:

create_graph_with_vertex() -> Graph = digraph:new(), digraph:add_vertex(Graph, csucs1, cimke1), Graph.

Egy csúcs és a címkéje bármilyen term lehet, a csúcsokat praktikus megoldás lehet numerikusan számozni. A harmadik, címke paraméter elhagyható, ekkor a csúcs címkéje az üres lista ([]) lesz. A második paraméter is opcionális, ekkor a függvény egy új csúcsot vesz fel a gráfba a ['$v' | N ] term által reprezentálva, ahol N természetes szám.

Gráfcsúcs lekérdezése

Egy adott gráfból a vertex függvénnyel kérdezhetjük le a paraméterül átadott csúcs címkéjét. A visszatérési érték nem csak a címke lesz, hanem a csúcs és címke rendezett párja. (Nem létező csúcs esetén a visszatérési érték a logikai hamis.)

Példa:

get_vertex_label(Graph, Vertex) -> Result = digraph:vertex(Graph, Vertex), if is_tuple(Result) -> element(2, Result); true -> false end.

Lehetőség van egy gráf összes csúcsának egy listában való lekérdezésére a vertices függvény által, azonban az eredmény listában a gráfcsúcsok rendezettségét tekintve semmilyen előfeltevéssel nem szabad élni.

A gráf csúcsainak számát a no_vertices függvény adja meg.

Gráfcsúcs fokszámának lekérdezése

Irányított gráfokról lévén szó, a csúcsoknak megkülönböztetjük a kimenő és bemenő fokszámát. Ezeket az out_degree valamint az in_degree függvényekkel kérdezhetjük le. Az első paraméter a gráf, a második pedig a vizsgált gráfcsúcs lesz.

Példa:

get_degree(Graph, Vertex) -> digraph:out_degree(Graph, Vertex) + digraph:in_degree(Graph, Vertex).

A gráfcsúcs teljes fokszámát az előbbi példának megfelelően a befok és a kifok összegeként kaphatjuk meg irányított gráf esetében, irányítatlan gráfnál pedig a két érték mindig egyenlő.

Gráfcsúcs szomszédságának lekérdezése

A digraph modul arra is beépítetten lehetőséget nyújt, hogy egy csúcspont összes kimenő, valamint bemenő szomszédos csúcsát lekérdezzük. Ez az információ könnyen előállítható, hiszen az élek csúcsok rendezett párjaként kerülnek ábrázolásra. A gráf és a vizsgált csúcs, mint paraméterek átadásával az out_neighbours és az in_neighbours függvények térne vissza a kimenő, illetve a bemenő szomszédok listájával. Az eredmény listában a gráfcsúcsok rendezettségét tekintve semmilyen előfeltevéssel nem szabad élni.

Amennyiben egy gráfcsúcs összes szomszédjára kíváncsiak vagyunk egy irányított gráfban, akkor uniózzuk a kimenő és bemenő szomszédokat a duplikátumok törlése mellett, például az itt látható módon.

get_neighbours(Graph, Vertex) -> umerge(sort(digraph:out_neighbours(Graph, Vertex)), sort(digraph:in_neighbours(Graph, Vertex))).

Irányítatlan gráfok esetében a bemenő és kimenő szomszédok természetesen megegyeznek, így erre nincs szükség.

Gráfcsúcs törlése

Egy gráfcsúcs törlésekor a digraph modul automatikusan törli a csúcsból kiinduló, illetve a csúcsba beérkező éleket egyaránt, így a gráf semmiképp sem kerülhet inkonzisztens állapotba. Egy csúcsot a delete_vertex, csúcsok listáját pedig a delete_vertices függvénnyel távolíthatunk el a gráfból. A törlendő csúcsot, illetve csúcsokat a második paraméterként adhatjuk át, az első argumentum pedig a gráf.

Példa:

remove_vertex(Graph, Vertices) when is_list(Vertices) -> digraph:delete_vertices(Graph, Vertices); remove_vertex(Graph, Vertex) -> digraph:delete_vertex(Graph, Vertex).

Gráfélek kezelése

A digraph modul függvényei által lehetőség van élek felvételére, módosítására és törlésére, illetve az éleknek és címkéjüknek a lekérdezésére. Ezen alapműveletek mellett a könyvtár olyan összetettebb feladatokra is kész függvényeket nyújt, mint a két csúcs közötti út keresése, vagy egy adott csúcson áthaladó kör keresése. Ezeket a műveleteket ráadásul elvégezhetjük úgy is, hogy a leggyorsabban megtalálható vagy a legrövidebb utat, illetve kört keressük.

Gráfél felvétele

Egy meglévő gráf élei közé újat felvenni, illetőleg a meglévőket módosítani egyaránt az add_edge függvénnyel lehetséges. A függvény első paraméterének a gráfot, a másodiknak az élet, a harmadiknak és negyediknek az él kiindulási és beérkezési csúcsát, az ötödiknek pedig az él címkéjét kell megadni. A függvény visszatérési értéke a létrehozott (vagy módosított) él.

Példa:

create_graph_with_edge() -> Graph = digraph:new(), digraph:add_vertex(Graph, csucs1, cimke1), digraph:add_vertex(Graph, csucs2, cimke2), digraph:add_edge(Graph, el1, csucs1, csucs2, cimke3), Graph.

Egy él és a címkéje bármilyen term lehet, de az éleket praktikus megoldás lehet numerikusan számozni. A második paraméter, az él megadása opcionális, ekkor a függvény egy új élt vesz fel a gráfba a ['$e' | N ] term által reprezentálva, ahol N természetes szám. Amennyiben az él termjének megadását elhagytuk, akkor az él címkéjét sem kötelező megadni. Ez esetben a címke az üres lista ([]) lesz.

Ha olyan élt vennénk fel egy gráfba, amelynek egyik megadott végpontja az adott gráfban nem létezik, vagy az él felvételével egy aciklikus gráfban kört hoznánk létre, akkor a függvény visszatérési értéke a hibát leíró tuple.

Gráfél lekérdezése

Egy adott gráfból az edge függvénnyel kérdezhetjük le a paraméterül átadott él címkéjét. A visszatérési érték nem csak a címke lesz, hanem az él, a két végcsúcs és a címke rendezett négyese.

Példa:

get_edge_label(Graph, Edge) -> Result = digraph:edge(Graph, Edge), if is_tuple(Result) -> element(4, Result); true -> false end.

Lehetőség van egy gráf összes élének egy listában való lekérdezésére az edges függvény által. A függvény első paramétere mindenképpen a gráf lesz, de ha a második, opcionális paraméterként egy gráfcsúcsot megadunk, akkor csak azoknak az éleknek a listáját kapjuk vissza, amelyek a megadott csúcsból indulnak ki, vagy oda érkeznek be. Az eredmény listában egyik esetben sem szabad az élek rendezettségét tekintve bármilyen előfeltevéssel élni.

A gráf éleinek számát a no_edges függvény adja meg.

Út keresése

Két féle képpen kereshetünk egy gráfban utat két megadott csúcs között. A get_path függvény mélységi keresést folytat, és az első megtalált utat adja vissza. A get_short_path függvény ezzel szemben szélességi keresést folytat, és szintén az első megtalált úttal tér vissza. Az algoritmus működéséből fakadóan garantált, hogy ennél rövidebb hosszúságú út nincsen a két megadott csúcs között a gráfban.

Mindkét függvény három paramétert vár: a gráfot, a kiindulási csúcsot, valamint a cél csúcsot. A visszatérési értékük a megtalált út csúcsainak rendezett listája, vagy logikai hamis, ha nincsen út a két csúcs között.

Kör keresése

Kört szintén két féle képpen, az úthoz igen hasonló módon kereshetünk egy gráfban egy megadott csúcsra illeszkedően. Mindkét függvény két paramétert vár: a gráfot, valamint azt a csúcsot, amit tartalmazni kell a körnek.

A get_cycle függvény előbb egy legalább 2 hosszúságú kört keres a gráfban, és az első megtalált csúcsinak rendezett listájával tér vissza. Ha nem talált ilyen kört, akkor megvizsgálja, hogy van egy hurokél a vizsgált csúcson és ha igen, akkor az adott csúccsal, mint egy elemű listával ([V]) tér vissza. A visszatérési érték egyéb esetben a logikai hamis.

A get_short_cycle ezzel szemben a megadott csúcson átvezető legrövidebb kört próbálja megkeresni - a hurokélt is beleértve. Ha nincsen a csúcson átvezető él, akkor a visszatérési érték a logikai hamis. Fontos, hogy ez a függvény a hurokélt, mint kételemű listát ([V, V]) adja vissza.

Gráfél törlése

Egy gráf egy élét a delete_edge, élek listáját pedig a delete_edges függvénnyel távolíthatunk el a gráfból. Az törlendő élt, illetve éleket a második paraméterként adhatjuk át, az első argumentum pedig a gráf.

Példa:

remove_edge(Graph, Edges) when is_list(Edges) -> digraph:delete_edges(Graph, Edges); remove_edge(Graph, Edge) -> digraph:delete_edge(Graph, Edge).
Út törlése

A digraph modul lehetőséget kínál arra, hogy egy G = (V, E) gráfbeli két csúcs között az összes utat töröljük a del_path függvénnyel. A függvény első argumentuma a gráf, második és harmadik paramétere pedig a kiindulási és a cél csúcs. Megjegyzendő azonban a törlési művelet működése: az algoritmus kiválaszt egy tetszőleges v1, v2, ..., vk utat a kiindulási gráfcsúcstól a célig, és törli az összes (vi, vi+1) élt E-ből (1 <= i < k), a párhuzamosakat is. Ezt addig ismétli, amíg van út a két megadott csúcs között.

Gráf törlése

Egy gráf törlésére a használata után azért van szükség, mert a digraph modul a gráfok implementációját ETS táblákkal valósítja meg, amelyekre nincsen szemétgyűjtés. Ha nem történik meg egy gráfra a delete függvény meghívása, akkor az őt létrehozó processz végén törlődik.

További segédfüggvények

A digraph_utils modul a digraph modul kiegészítéseként funkcionál: összetett, de gyakran használt és igen hasznos gráfalgoritmusok implementációját tartalmazza. A részletes leírásért lásd a hivatalos referencia oldalt.

Kulcs-érték párok

Az Erlang nyelvben több lehetőségünk is van kulcs-érték párok kezelésére. Ezek közül kettőt ismertetünk részletesen.

A fejezethez készített mintaprogram megtalálható és letölthető a példaprogramok között.

Property listák

A property lista adattípus egy két elemű tuple-ket tartalmazó lista. Az egyes tuple-k első eleme a kulcs, a második az adott kulcshoz tartozó érték. A kulcsok és az értékek is term típusúak. Ha a listában egy kulcs többször is előfordul, akkor az első előfordulásához tartozó értéket veszi figyelembe a property lista.

A proplists modul beépített függvényei

delete(Kulcs, Lista) -> Lista

Törli a paraméterként kapott listából az adott kulcs összes előfordulását.

> proplists:delete(a,[{a,1},{b,2},{a,3}]). [{b,2}]

get_all_values(Kulcs, Lista) -> [term()]

Ez a függvény visszaadja a kapott listában az adott kulcshoz létező összes értéket.

> proplists:get_all_values(a,[{a,1},{b,2},{a,3}]). [1,3]

get_keys(Lista) -> [term()]

Ez a függvény visszaadja a kapott listában előforduló összes kulcsot.

> proplists:get_keys([{a,1},{b,2},{a,3}]). [a,b]

get_value(Kulcs, Lista) -> term()

Ez a függvény megadja az adott kulcshoz tartozó értékek közül az elsőt.

> proplists:get_value(a,[{a,1},{b,2},{a,3}]). 1

is_defined(Kulcs, Lista) -> boolean()

Ezen függvény segítségével meghatározhatjuk, hogy az adott kulcs szerepel-e a listában. Ha benne van a listában, akkor a függvény ’true’ értékkel tér vissza. Ha nincs a listában a kulcs, akkor a függvény ’false’ értékkel tér vissza.

> proplists:is_defined(a,[{a,1},{b,2},{a,3}]). true > proplists:is_defined(c,[{a,1},{b,2},{a,3}]). false

lookup(Kulcs, Lista) -> none | tuple()

Ezen függvény segítségével megkereshetünk egy kulcsot a listában. Ha létezik, akkor visszakapjuk az adott kulcs első előfordulásához tartozó kulcs-érték párt. Ha a kulcs nem létezik, akkor ’none’ értékkel tér vissza a függvény.

> proplists:lookup(a,[{a,1},{b,2},{a,3}]). {a,1} > proplists:lookup(c,[{a,1},{b,2},{a,3}]). none

lookup_all(Kulcs, Lista) -> [tuple()]

Ezen függvény segítségével megkaphatjuk egy adott kulcs összes előfordulását a listában. Ha a kulcs nincs a listában, akkor a függvény egy üres listával tér vissza.

> proplists:lookup_all(a,[{a,1},{b,2},{a,3}]). [{a,1},{a,3}] > proplists:lookup_all(c,[{a,1},{b,2},{a,3}]). []

Maps

Az Erlang R17-es verziójának kiadásával bővült az adattípusok halmaza, valamint átdolgozták az egész nyelvet, hogy könnyebb legyen az új típusok beépítése. A map típust a record típus helyettesítésére hozták létre.

A map típus kulcs-érték párok asszociatív gyűjteménye. Más nyelvekben is megtalálható ez az adattípus:

Az Erlang nyelvben a kulcs és az érték típusának is term-nek kell lennie.

Miért volt szükség erre az új típusra?
Maps műveletek

Map létrehozása:

    Map = #{Kulcs1 => Érték1, Kulcs2 => Érték2, ...}

Használhatunk mintaillesztést is:

    #{Kulcs1 := Minta1, Kulcs2 := Minta2, ...} = Map

Map frissítése:

    ÚjMap = Map#{ Kulcs1 => Érték1, ... KulcsN := ÉrtékN, ...}

A frissítésre két operátor is rendelkezésünkre áll: „=>” és „:=”, de ezeknek nem ugyanaz a jelentése:

A maps modul beépített függvényei

find(Kulcs, Map) -> {ok, Érték} | error

Megkeres egy kulcshoz tartozó értéket a map-ben. Ha a kulcs létezik, akkor visszaadja a hozzátartozó értéket. Ha a kulcs nem létezik, akkor hibával tér vissza a függvény.

> Map = #{"azonosito" => 42}, Kulcs = " azonosito ", maps:find(Kulcs,Map). {ok,42}

from_list(Lista) -> Map

Ezzel a függvénnyel egy kulcs-érték párokat tartalmazó listából hozhatunk létre egy map-et. Ha egy kulcs többször is szerepel a listában, akkor a legkésőbb előforduló érték fog hozzá tartozni a map-ben.

> Lista = [{"a",valami},{b,"masodik"},{42,harmadik},{"a",1}], maps:from_list(Lista). #{42 => harmadik,b => "masodik","a" => 1}

get(Kulcs, Map) -> Érték

Ha az adott kulcs benne van a map-ben, akkor a függvény a hozzá tartozó értékkel tér vissza. Ha nincs benne, akkor kivétel generálódik.

> Kulcs = b, Map = #{42 => masodik,b => "elso","a" => 1}, maps:get(Kulcs,Map). "elso"

is_key(Kulcs, Map) -> boolean()

Ezen függvény segítségével eldönthető, hogy az adott kulcsot tartalmazza-e a map. Ha igen, akkor ’true’ értékkel tér vissza. Ha nem, akkor pedig ’false’ lesz a függvény visszatérési értéke.

> Map = #{"42" => valami}. #{"42"> => valami} > maps:is_key("42",Map). true > maps:is_key(valami,Map). false

keys(Map) -> Kulcsok

Ez a függvény a mapben megtalálható összes kulcsot adja vissza egy lista formájában.

> Map = #{42 => elso,b => "masodik","a" => 1}, maps:keys(Map). [42,b,"a"]

map(Fun, Map1) -> Map2

Ezzel a függvényel lehetőségünk van egy map-et létrehozni egy másik map-ből egy fun expression segítségével. A fun expression segítségével a régi map minden elemén végrehajtódik ugyanaz a művelet. A példában minden érték kétszerese fog szerepelni az új map-ban.

> Fun = fun(K,V1) when is_list(K) -> V1*V1 end, Map = #{"k1" => 1, "k2" => 2, "k3" => 3}, maps:map(Fun,Map). #{"k1" => 1,"k2" => 4,"k3" => 9}

merge(Map1, Map2) -> Map3

Ezzel a függvényével egyesíthetünk két map-et. Ha van olyan kulcs, ami mindkét map-ben szerepel, akkor az ahhoz a kulcshoz tartozó érték a második paraméterbeli értéket kapja.

> Map1 = #{a => "elso", b => "masodik"}, Map2 = #{a => 1, c => 2}, maps:merge(Map1,Map2). #{a => 1,b => "masodik",c => 2}

new() -> Map

Ezzel a függvénnyel egy új, üres map-et hozhatunk létre.

> maps:new(). #{}

put(Kulcs, Érték, Map1) -> Map2

Ezzel a függvénnyel egy új kulcs-érték párt tehetünk a map-be. Ha a megadott kulcs már létezik, akkor a paraméterként kapott érték fog hozzátartozni. A függvény egy új map-pel fog visszatérni.

> Map = #{"a" => 1}. #{"a" => 1} > maps:put("a", 42, Map). #{"a" => 42} > maps:put("b", ketto, Map). #{"a" => 1,"b" => ketto}

remove(Kulcs, Map1) -> Map2

Ezzel a függvénnyel eltávolíthatunk egy elemet a map-ből. Ha a megadott kulcs létezik akkor a visszatérési értékben már nem fog szerepelni a paraméterként megadott kulcsot tartalmazó pár.

> Map = #{"a" => 1}. #{"a" => 1} > maps:remove("a",Map). #{} > maps:remove("b",Map). #{"a" => 1}

size(Map) -> integer() >= 0

Ez a függvény a map elemeinek számát adja meg.

> Map = #{42 => elso,b => "masodik"}, maps:size(Map). 2

to_list(Map) -> [{Kulcs, Érték}]

Ez a függvény egy kulcs-érték párokból álló listát hoz létre egy map-ből.

> Map = #{42 => harmadik,b => "masodik","a" => 1}, maps:to_list(Map). [{42,harmadik},{b,"masodik"},{"a",1}]

update(Kulcs, Érték, Map1) -> Map2

Ha a paraméterként kapott kulcs benne van a map-ben, akkor a hozzá tartozó értéket lecseréli a paraméterként kapott értékre. Ha a kapott kulcs nem létezik, akkor kivétel generálódik.

> Map = #{"a" => 1}. #{"a" => 1} > maps:update("a", 42, Map). #{"a" => 42}

values(Map) -> Értékek

Ez a függvény a paraméterként kapott map-ben található összes értéket adja vissza egy lista formájában.

> Map = #{42 => harmadik,b => "masodik","a" => 1}, maps:values(Map). [harmadik,"masodik",1]

without(Kulcsok, Map1) -> Map2

Ez a függvény az első paraméterként kapott listában szereplő kulcsokat kiveszi a második paraméterként kapott map-ből. Ha a listában olyan kulcs is szerepel, amely nem található meg a map-ben, akkor azt a függvény figyelmen kívül hagyja.

> Map = #{42 => harmadik,b => "masodik","a" => 1}, Kulcsok = ["a",42,"valami"], maps:without(Kulcsok,Map). #{b => "masodik"}

Eseménykezelés (gen_event modul)

A témakörhöz egy példaprogramot találhatunk a Könyvtár linken.

Leírás

A gen_event egy viselkedésminta modul, ami az eseménykezelést teszi lehetővé. Az Erlang OTP eseménykezelő modellje egy generikus eseményvezérlő (event manager) folyamatból, valamint tetszőleges számú eseménykezelőből (ezek dinamikusan adódnak, és törlődnek a vezérlőből) áll.

Minden eseménykezelő egy "callback" modulként kerül megvalósításra, amely egy előre definiált interfészt valósít meg. Minden eseményvezérlő, amit a gen_server modul használatával valósítunk meg, olyan függvényekre támaszkodik, amelyek kapcsolatban állnak az eseménykezelő modulok interfészével. A vezérlő és a kezelő függvényei közötti kapcsolata az alábbi:

gen_event module Callback module ---------------- --------------- gen_event:start_link -----> - gen_event:add_handler gen_event:add_sup_handler -----> Module:init/1 gen_event:notify gen_event:sync_notify -----> Module:handle_event/2 gen_event:call -----> Module:handle_call/2 - -----> Module:handle_info/2 gen_event:delete_handler -----> Module:terminate/2 gen_event:swap_handler gen_event:swap_sup_handler -----> Module1:terminate/2 Module2:init/1 gen_event:which_handlers -----> - gen_event:stop -----> Module:terminate/2 - -----> Module:code_change/3

Mivel minden eseménykezelő egyetlen "callback" modul, egy eseményvezérlőhöz több is tartozhat, és ezek futás közben dinamikusan hozzáadhatók és törölhetők. Következésképpen a gen_event sokkal kevésbé érzékeny a "callback" modulok hibáira, mint a többi viselkedésminta. Amennyiben egy hozzáadott eseménykezelő "callback" függvénye Reason eredményű hibával, vagy nem megfelelő értékű termmel tér vissza, a vezérlő tovább működik. Ilyen esetben csupán meghívja az érintett modul terminate/2 függvényét {error,{'EXIT',Reason}} vagy {error,Term} argumentummal, és ezzel törli az eseménykezelőt, anélkül, hogy ez bármilyen befolyással lenne a többi eseménykezelő működésére.

A gen_event folyamat hibernálható, amennyiben valamelyik eseménykezelő modul "callback" függvényének visszatérési értéke tartalmazza a 'hibernate' kulcsszót. Ez kifejezetten hasznos lehet, ha a szerver hosszú ideig tétlen maradhat. Ennek használata csak indokolt esetben javasolt, ugyanis egy hibernálás legalább két "szemétgyűjtést" eredményez (hibernáláskor, és rögtön az ébredés után), és nem kifizetődő minden esemény kezelésekor végigcsinálni.

Érdemes megjegyezni, hogy amennyiben több eseménykezelő is jelen van, az is elegendő, ha csak egyikük kéri a hibernálást, nem kell mindegyiküknek megtennie ezt.

Adattípusok

handler() = atom() | {atom(), term()} handler_args() = term() add_handler_ret() = ok | term() | {'EXIT', term()} del_handler_ret() = ok | term() | {'EXIT', term()}

Exportált függvények

start_link() -> Result
start_link(EventMgrName) -> Result

Típusai:

EventMgrName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}
  Name = atom()
  GlobalName = ViaName = term()
Result = {ok,Pid} | {error,{already_started,Pid}}
  Pid = pid()

Egy eseményvezérlő processzt készít a "supervisor tree" részeként. A függvényt a supervisor hívja közvetve, vagy közvetlenül, és ez biztosítja többek között azt, hogy a vezérlő hozzá fog kötődni.

Ha EventMgrName={local,Name}, az eseményvezérlő lokálisan regisztrált, Name névvel a register/2 használatával. Ha EventMgrName={global,GlobalName}, az eseményvezérlő globálisan került regisztrálásra GlobalName néven, a global:register_name/2használatával. Ha nincs név, a vezérlő regisztrálatlan. Ha EventMgrName={via,Module,ViaName}, a vezérlő a Module regisztrációs felületét használja a regisztrációjához, ezért a Module-nak exportálnia kell a register_name/2, unregister_name/1, whereis_name/1 and send/2 függvényeket, amelyeknek a globális megfelelőjükhöz hasonlóan kell viselkedniük. Következésképp a {via,global,GlobalName} helyes hivatkozás.

Amennyiben az eseméynvezérlő sikeresen létrejön, a függvény az {ok,Pid} eredménnyel tér vissza, amiben a Pid a vezérlő processz azonosítója. Ha már létezik EventMgrName nevű processz, a visszatérési érték {error,{already_started,Pid}} lesz, az érintett processz azonosítójával.

start() -> Result
start(EventMgrName) -> Result

Típusai:

EventMgrName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}
  Name = atom()
  GlobalName = ViaName = term()
Result = {ok,Pid} | {error,{already_started,Pid}}
  Pid = pid()

Egy supervisor nélküli, önálló eseményvezérlő processzt indít.

add_handler(EventMgrRef, Handler, Args) -> Result

Típusai:

EventMgr = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
  Name = Node = atom()
  GlobalName = ViaName = term()
Handler = Module | {Module,Id}
  Module = atom()
  Id = term()
Args = term()
Result = ok | {'EXIT',Reason} | term()
  Reason = term()

Egy új eseménykezelőt ad az EventMgrRef vezérlőhöz, ami meghívja a Module:init/1 függvényt, hogy inicializálja a kezelőt.

Az EventMgrRef értékei a következők lehetnek:

A "callback" modul neve Handler, vagy {Module,Id}, ahol Id bármilyen term lehet. Az utóbbi reprezentáció lehetővé teszi, hogy azonosítsunk különböző kezelőket, amelyek ugyanazt a "callback" modult használják.

Az Args egy tetszőleges term, ami a Module:init/1 függvény argumentuma lesz.

Ha a Module:init/1 megfelelő eredményt ad vissza jelezvén, hogy minden rendben ment, az eseményvezérlőhöz hozzáadódik a kezelő, és ez a függvény egy ok atommal tér vissza. Ha Module:init/1 hibásan fut le, melynek oka Reason, vagy {error,Reason} eredménnyel tér vissza, a kezelő elutasításra kerül, és a függvény visszatérési értéke {'EXIT',Reason} vagy {error,Reason} lesz.

add_sup_handler(EventMgrRef, Handler, Args) -> Result

Típusai:

EventMgr = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
  Name = Node = atom()
  GlobalName = ViaName = term()
Handler = Module | {Module,Id}
  Module = atom()
  Id = term()
Args = term()
Result = ok | {'EXIT',Reason} | term()
  Reason = term()

Az add_handler/3 függvényhez hasonlóan egy eseménykezelőt ad a vezérlőhöz, azonban a kezelő és a hívó processz kapcsolatát is felügyeli.

Ha a hívó processz később terminál Reason eredménnyel, a vezérlő törli a kezelőt, meghívva a Module:terminate/2 függvényt {stop,Reason} argumentummal. Ha a kezelő kerül törlésre, az eseményvezérlő egy {gen_event_EXIT,Handler,Reason} üzenetet küld a hívó processznek, ahol Reason értéke az alábbiak egyike:

notify(EventMgrRef, Event) -> ok
sync_notify(EventMgrRef, Event) -> ok

Típusai:

EventMgrRef = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
  Name = Node = atom()
  GlobalName = ViaName = term()
Event = term()

Egy esemény-értesítőt küld az EventMgrRef eseményvezérlő számára, ami ekkor az összes csatlakozott kezelő Module:handle_event/2 függvényét meghívja.

A notify aszinkron, és azonnal visszatér, amint az esemény-értesítő ki lett küldve. A sync_notify ezzel szemben szinkron hívás, abban az értelemben, hogy csak akkor tér vissza, ha az összes eseménykezelő feldolgozta az adott eseményt.

Az Event egy tetszőleges term, amit argumentumként kap meg a Module:handle_event/2.

call(EventMgrRef, Handler, Request) -> Result
call(EventMgrRef, Handler, Request, Timeout) -> Result

Típusai:

EventMgrRef = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
  Name = Node = atom()
  GlobalName = ViaName = term()
Handler = Module | {Module,Id}
  Module = atom()
  Id = term()
Request = term()
Timeout = int()>0 | infinity
Result = Reply | {error,Error}
  Reply = term()
  Error = bad_module | {'EXIT',Reason} | term()
    Reason = term()

Szinkron hívást intéz az EventMgrRef eseményvezérlő Handler eseménykezelőjéhez, elküldve a kérést, majd várva a válasz érkezésére, vagy időtúllépésre. A vezérlő a Module:handle_call/2 függvényt hívja meg a kérés teljesítésére.

A Request egy testzőleges term, ami a Module:handle_call/2 argumentuma lesz.

A Timeout lehet egy pozitív egész szám, ami milliszekundumbanés határozza meg a válaszra várakozás időkorlátját, vagy lehet az infinity atom, ami határozatlan idejű várakozást eredményez. Az alapértelmezett érték 5000. Ha nincs válasz a megadott időn belül, a függvény hívás hibát eredményez.

A Reply eredmény a Module:handle_call/2 visszatérési értékében kerül definiálásra. Ha a megcélzott eseménykezelő nincs hozzáadva a vezérlőhöz a függvény a {error,bad_module} eredménnyel tér vissza. Ha a "callback" függvény Reason eredményű hibás futást produkál, vagy egy nem várt Term értékkel tér vissza, akkor ez a függvény a {error,{'EXIT',Reason}} vagy a {error,Term} válasszal tér vissza.

delete_handler(EventMgrRef, Handler, Args) -> Result

Típusai:

EventMgrRef = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
  Name = Node = atom()
  GlobalName = ViaName = term()
Handler = Module | {Module,Id}
  Module = atom()
  Id = term()
Args = term()
Result = term() | {error,module_not_found} | {'EXIT',Reason}
  Reason = term()

Töröl egy eseménykezelőt az EventMgrRef eseményvezérlőből, ekkor a vezérlő meghívja a Module:terminate/2 függvényt, hogy a kezelő termináljon.

Az Args egy tetszőleges term, ami a Module:terminate/2 argumentuma lesz.

A visszatérési érték a Module:terminate/2 függvény eredménye. Ha a megcélzott kezelő nincs a vezérlőhöz adva, az eredmény: {error,module_not_found}. Ha a "callback" függvény Reason eredményű hibás futást produkál, az eredmény: {'EXIT',Reason}.

swap_handler(EventMgrRef, {Handler1,Args1}, {Handler2,Args2}) -> Result

Típusai:

EventMgrRef = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
  Name = Node = atom()
  GlobalName = ViaName = term()
Handler1 = Handler2 = Module | {Module,Id}
  Module = atom()
  Id = term()
Args1 = Args2 = term()
Result = ok | {error,Error}
  Error = {'EXIT',Reason} | term()
    Reason = term()

Lecserél egy régi eseménykezelőt egy újra az EventMgrRef eseményvezérlőben.

Első lépésként a régi eseménykezelő, Handler1 törlődik. A vezérlő meghívja a Module1:terminate(Args1, ...) függvényt, ahol a Module1 a Handler1 "callback" modulja, és begyűjt egy visszatérési értéket. Ezután a Module2:init({Args2,Term}) hívással, a Handler2 kerül hozzáadásra és inicializálásra, ahol a Module2 a Handler2 kezelő "callback" modulja, a Term pedig a Module1:terminate/2 visszatérési értéke. Ez lehetővé teszi a régi kezelőből az újba történő információküldést.

Ha a régi kezelő nincs a vezérlőhöz adva, az új akkor is hozzáadódik, de ebben az esetben Term=error. Ha Module1:terminate/2 hibát eredményez Reason-nal, akkor Term={'EXIT',Reason}. A régi kezelő abban az esetben is törlődik, ha a Module2:init/1 hibát eredményez.

Ha felügyelt kapcsolat volt a Hadnler1 és egy Pid processz között, akkor ezt felváltja egy Handler2 és Pid közötti felügyelt kapcsolat.

Ha a Module2:init/1 helyes értékkel tér vissza, az eredmény ok. Reason hiba, vagy nem várt értékű Term esetén a függvény a {error,{'EXIT',Reason}}, vagy {error,Term} eredményt adja.

swap_sup_handler(EventMgrRef, {Handler1,Args1}, {Handler2,Args2}) -> Result

Típusai:

EventMgrRef = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
  Name = Node = atom()
  GlobalName = ViaName = term()
Handler1 = Handler 2 = Module | {Module,Id}
  Module = atom()
  Id = term()
Args1 = Args2 = term()
Result = ok | {error,Error}
  Error = {'EXIT',Reason} | term()
    Reason = term()

A swap_handler/3-hoz hasonlóan lecserél egy eseménykezelőt az EventMgrRef eseményvezérlőben, de a Hadnler2 és a hívó processz közötti kapcsolat is felügyelet alá kerül.

which_handlers(EventMgrRef) -> [Handler]

Típusai:

EventMgrRef = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
  Name = Node = atom()
  GlobalName = ViaName = term()
Handler = Module | {Module,Id}
  Module = atom()
  Id = term()

Az EventMgrRef eseményvezérlő eseménykezelőinek listáját adja eredményül.

stop(EventMgrRef) -> ok

Típusai:

EventMgrRef = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
Name = Node = atom()
GlobalName = ViaName = term()

Terminálja az EventMgrRef eseményvezérlőt, ám ezelőtt meghívódik az összes hozzáadott kezelő Module:terminate(stop,...) függvénye.

Egyéb, itt nem részletezett könyvtárak

Részletesebb leírásért lásd az ERLANG hivatalos oldalát.