A Pike programozási nyelv

Típusok, típuskonstrukciók

Elemi típusok

A nyelv alapvetően kétféle adattípust tud kezelni:

    Egyszerű (basic types)
    Mutató (pointer types)
Az egyszerű típusoknál (int, float, string) az értékadásnál az érték másolódik át, míg a mutató típusoknál csak a mutató. A dinamikusan létrehozott mutató típusú adataink után a memóriát nem kell felszabadítanunk, ezt a Garbage collector elvégzi helyettünk.

Egyszerű típusok

int

32 biten tárolt előjeles egész szám. Használható rá minden beépített aritmetikai, bitenkénti és logikai operátor. Nincs a nyelvben külön logikai típus, ennek megvalósítása egy az egyben az int típussal történik (0 - hamis, 1 - igaz).
Az egész literálok szintaktikája:

Minta

Példa

Leírás

-?[1-9][0-9]*

78

Decimális szám

-?0[0-9]*

0116

Oktális szám

-?0[xX][0-9a-fA-F]+

0x4e

Hexadecimális szám

-?0[bB][01]+

0b1001110

Bináris szám

-?'\\?.'

'N'

ASCII karakter

A fentiek mindegyike a 78-as decimális számot jelenti.

Egész osztás van a nyelvben, és ez mindig az alsó egész részt jelenti. Egész hatványozás is van: int pow(int alap,int kit)

Egyéb függvények:

int intp(mixed x) 1-gyel tér vissza, ha x egy int, egyébként 0-val.
int random(int x) 0<=szám<x véletlen értékkel tér vissza.
int reverse(int x) Megfordítja x bitjeit. (Mire való?!)
int sqrt(int x) x gyöke.

float

32 biten tárolt lebegőpontos szám, mely 9 jegy pontosságú. Ha nagyobb pontossággal definiálunk egy literált, akkor azt a legtöbb architektúrán levágja.

A float literálok lehetséges megadásai:

Minta

Példa

Mit jelent?

-?[0-9]+\.[0-9]+

3.1415926

3.1415926

-?[0-9]+e-?[0-9]+

-5e3

-5000.0

-?[0-9]+\.[0-9]+e-?[0-9]+

1.022e-2

0.01022

Használható rá aritmetikai és logikai operátor, és a következő függvények:

Trigonometrikus függvények

A trigonometrikus függvények: szinusz(sin), arkusz szinusz(asin), koszinusz(cos), ark. kosz.(acos), tan és atan használhatóak.

float log(float x) x természetes alapú logaritmusával tér vissza.
float exp(float x) e-alapú hatványozás.
float pow(float|int x, float|int y) x^y -nal tér vissza.
float sqrt(float x) x gyökét adja. (mint az int-nél)
float floor(float x) A legnagyobb int érték, ami kisebb vagy egyenlő, mint x. Vigyázni: a visszatérési érték nem int, hanem float!
float ceil(float x) A legkisebb int érték, ami nagyobb vagy egyenlő, mint x. Ezt is float-ként!
float round(float x) Matematikai kerekítés.

string

A string, vagy karakterlánc tulajdonképpen 0-tól 232 - 1-ig terjedő értékek (illetve ilyen értékek által reprezentált karakterek) tömbje. Tartalmazhat szavakat, mondatokat, egy oldalt, vagy akár egy egész könyvet is, de tartalmazhatja egy bináris file részleteit, tömörített adatot, vagy egyéb bináris adatot. Pike-ban a karakterláncok osztottan tárolódnak, ami azt jelenti, hogy a megegyező karakterláncok ugyanazon a memóriahelyen tartózkodnak, így a legtöbb esetben jelentősen csökken a memóriahasználat és gyorsul a karakterláncok összehasonlítása.

Néhány példa:

"hello world" // hello world
"he" "llo" // hello
"\116" // N (116 oktális ASCII érték)
"\t" // tab karakter
"\n" // newline karakter
"\r" // kocsivissza karakter
"\b" // backspace karakter
"\0" // null karakter
"\"" // idézőjel
"\\" // backslash
"\x4e" // N (4e hexadecimális ASCII érték)
"\d78" // N (78 decimális ASCII érték)
"hello world\116\t\n\r\b\0\"\\" // vegyes
"\xff" // 255-ös karakter
"\xffff" // 65536-os karakter
"\xffffff" // 16777215
"\116""3" // 'N' után '3'

Szekvencia

ASCII kód

Karakter

\b

8

Backspace karakter

\t

9

Tabulátor karakter

\n

10

Újsor karakter

\r

13

Kocsivissza karakter

\"

34

Idézőjel karakter

\\

92

Backslash karakter

Minden, (dupla) idézőjelek közé zárt karaktersorozat string. A backslash karakter és escape szekvenciák segítségével lehet meg nem jeleníthető vagy be nem gépelhető karaktereket megadni, mint például \t-t a tabulátor, \\ -t a backslash, valamint \" -t a dupla idézőjel helyett. Ezen kívül \XXX alakban bármilyen 0 és 37777777777 közötti oktális, \xXX alakban bármilyen 0 és FFFFFFFF közötti hexadecimális, és \dXXX alakban bármilyen 0 és 232 - 1 közötti decimális kódú karakter megadása lehetséges.

Habár a 0 és 232 - 1 lehetséges érték memóriahasználat szempontjából soknak tűnhet, a Pike azonban automatikusan meghatározza a karakterláncban található karaktereknek szükséges tárat, így például egy 0 és 255 közötti kódú karakterekből álló string karakterenként csupán egy byte-ot foglal. Azonban vigyázni kell, mivel nem minden függvény képes az egy byte-os mérettől eltérő karaktereket kezelni.

Bármennyire is hasonlítson a karakterlánc típus egy tömbre, lényeges eltérés, hogy a karakterlánc nem változtatható meg. Ez annyit jelent, hogy még egy karakter cseréjére sincs mód új karakterlánc létrehozása nélkül. A korábbiak fényében ez érthető, hiszen különben az osztott tárolás miatt az összes azonos karaktereket tartalmazó karakterlánc megváltozna. Ennek ellenére az alábbihoz hasonló kód engedélyezett, és működik:

string s = "Hello torld"; s[6] = 'w';

Azonban ennek eredményeként egy új karakterlánc keletkezik, benne a megváltozott karakterrel. Ez egyben azt jelenti, hogy a fenti művelet hosszú karakterláncoknál meglehetősen lassú lehet.

Karakterláncokon az összehasonlítás operátorokon kívül még más operátorokat is használhatunk

Karakterláncokkal dolgoznak az alábbi függvények:

string String.capitalize(string s) s-nek első betűjét nagybetűre cseréli.
int String.count(string haystack, string needle) Visszaadja, hogy hányszor fordul elő átfedés nélkül a needle a haystack-ben.
int String.width(string s) Visszaadja s szélességét, azaz azt, ahány biten s karakterei tárolódnak (8, 16, vagy 32)
string lower_case(string s) s kisbetűsített változatát adja vissza.
string replace(string s, string mit, string mire) Kicseréli s-ben mit mire-re.
string reverse(string s) s fordítottját adja vissza.
int search(string haystack, string needle) Megkeresi a needle-t a haystack-ben, és visszaadja az első előfordulás helyét.
int strlen(string s) s hosszát adja vissza.
int sizeof(string s) Az strlen-hez hasonlóan s hosszát adja vissza.
int stringp(mixed s) 1-et ad vissza, ha string, 0-t különben.
string upper_case(string s) s nagybetűsített változatát adja vissza.

Mutató típusok

A Pike mutató típusoknak nevezi az összetett típusokat: a tömböt (array), az asszociatív tárolót (mapping), a multihalmazt (multiset), a programot, az objektumot és a függvényt. Ezek mind mutatók, azaz egy memóriában található "dologra" mutatnak, egyre akár többen is, és ez a "dolog" akkor szabadul fel, ha nem mutat rá több mutató. Így tehát értékadáskor sem a memóriában található "dolog" másolódik át, csupán a rá mutató referencia.

Különös gonddal kell eljárni függvények mutatóval való paraméterezésekor, ugyanis ekkor a függvény képes a memóriacímen található "dolog" megváltoztatására. Ezt elkerülendő explicit módon lehetséges a "dolog" közvetlen, érték szerinti átadása.

tömb(array)

A tömbök a legegyszerűbb mutatótípusok. Maga a tömb csupán a memória egy fix méretű darabja, mely bármilyen adat tárolására képes mezőkből áll. Ezek a mezők az elemek, és az indexelés operátoron keresztül érhetők el. Konstans tömb a ({ és }) jelek közé zárva hozható létre, mint például a következők:

({ }) // Üres tömb
({ 1 }) //Egy int típusú értéket tartalmazó tömb
({ "" }) //Egy stringet tartalmazó tömb
({ "", 1, 3.0 }) //Három különböző típusú elemet tartalmazó tömb

Mint látható, a tömb bármelyik eleme bármilyen típusú adatot tárolhat. Indexelés és részsorozatképzés a karakterláncokhoz hasonlóan történik, azzal a különbséggel, hogy az indexelés operátorral megváltoztatható a tömb adott eleme. Nem változtatható viszont a tömb mérete, tehát ha új elemek hozzáadására van szükség, az egész tömböt hozzá kell adni egy másikhoz, mellyel egy új tömb jön létre.

Tömbökkel használható operátorok és függvények.

Az indexelés visszaadja vagy megváltoztatja a tömb adott elemét. A c indexnek integer típusúnak kell lennie. A tömb értékének megváltoztatásához egyszerűen tegyük az egészet az értékadás bal oldalára, például így: t [ c ] = uj_ertek

indexelés(t [ c ])

részsorozat(t [ tol .. ig ])

A részsorozat a t tol, tol + 1, .., ig indexű elemeit egy új tömbbe másolja, melynek a mérete így ig - tol + 1 lesz.

összehasonlítás(a == b illetve a != b)

Az egyenlőséget vizsgáló operátor visszatérési értéke 1 akkor és csak akkor, ha a és b ugyanaz a tömb. Tehát nem elég, ha méretük és tartalmuk megegyezik, a két mutatónak ugyanarra a tömbre kell mutatnia. Például ({1}) == ({1}) visszatérési értéke 0, míg array(int) a=({1}); return a == a; visszatérési értéke 1. Figyelem, tömbökön nem használhatók a >, >=, <, vagy <= operátorok.

Összeadás(a + b)

A karakterláncokhoz hasonlóan az összeadás egymáshoz fűzi, konkatenálja a tömböket. ({1})+({2}) visszatérési értéke így ({1,2}).

Kivonás (a - b)

A kivonás az első tömbből eltávolítja a másodikban előforduló összes elemet. Tehát például {1,3,8,3,2}) - ({3,1}) eredménye ({8,2}).

Metszet (a & b)

A metszet eredménye azon elemek tömbje, amelyek mind a-ban, mind b-ben előfordulnak. Az elemek sorrendje olyan lesz, ahogy a-ban előfordultak. Például ({1,3,7,9,11,12}) & ({4,11,8,9,1}) eredménye ({1,9,11}).

Unió (a | b)

Az unió majdnem ugyanazt eredményezi mint az összeadás, de csak azokat az elemeket adja hozzá a-hoz, melyek még nem szerepelnek benne. Tehát ({1,2,3}) | ({1,3,5}) eredménye ({1,2,3,5}). Megjegyzés: a elemeinek sorrendje megváltozhat.

Kizáró vagy (a ^ b)

A kizáró vagy, illetve szimmetrikus differencia eredménye azon elemek tömbje, melyek a-ban vagy b-ben vannak, de nem fordulnak elő mindkettőben egyszerre. Például ({1,3,5,6}) ^ ({4,5,6,7}) eredménye ({1,3,4,7}).

Felosztás, osztás (a / b)

Az osztás eredmény olyan tömbök tömbje, mely ha b szintén tömb, akkor a részeit b előfordulásainál felosztva tartalmazza, viszont ha b int vagy float, akkor minden b-edik elemnél osztja fel a-t. Tehát például {1,2,3,4,5}) / ({2,3}) eredménye ({ ({1}), ({4,5}) }), és {1,2,3,4}) / 2 eredménye ({ ({1,2}), ({3,4}) }).

Maradék, modulo (a % b)

Ez csak akkor érvényes, ha b integer, és ekkor azt a maradékot adja vissza, ami nem fordul elő a / b-ben.

array aggregate(mixed ... elems)

Eredménye a ({ }) operátoréval megegyező, azaz a paraméterekből képzett tömbbel tér vissza. Tehát például ({1,2,3}) eredménye megegyezik az aggregate(1,2,3)-éval.

array allocate(int meret)

Lefoglal egy meret méretű tömböt, melynek elemei kezdetben mind 0-k.

int arrayp(mixed a)

1-el tér vissza, ha a paraméter tömb, 0-val különben.

array column(array(mixed) a, mixed ind)

E függvény végigmegy a elemein, mindegyiket ind-del indexeli, és az eredményből épített tömböt adja vissza. Tehát például column( ({ ({1,2,3}), ({4,5,6}), ({7,8,9}) }), 2) eredménye ({3,6,9}).

int equal(mixed a, mixed b)

Két tömb méret- és tartalombeli egyezőségét vizsgálja (tehát nem kell azonos memóriacímen elhelyezkedniük).

array filter(array a, mixed func, mixed ... args)

A filter a azon elemeit adja vissza, melyre a func-ot első paraméterként az adott elemmel, a maradék paraméterbe pedig args-t helyettesítve az eredmény igaz.

array map(array a, mixed func, mixed ... args)

A map a filter-hez hasonlóan működik, ám ez a func függvényt az előző módon meghívva, annak eredményeinek tömbjét adja vissza.

array replace(array a, mixed mit, mixed mire)

a-nak olyan másolatát készíti el, melyben minden mit elemet mire-re cserél.

array reverse(array a)

Megfordítja a elemeinek sorrendjét.

array rows(array a, array indexek)

E függvény működése a column-éhoz hasonló. a-t az indexek tömb elemeivel indexeli, és az eredmények tömbjét adja vissza. Például rows( ({"a","b","c"}), ({ 2,1,2,0}) ) eredménye ({"c","b","c","a"}).

int search(array szenakazal, mixed tu)

Azzal az indexszel tér vissza, ahol a tu először előfordul a szenakazal-ban (egyenlőségvizsgálatnak ==-t használ).

int sizeof(mixed tomb)

A tomb tömb elemeinek számát adja vissza.

array sort(array tomb, array ... maradek)

E függvény tomb-ot rendezi növekvő sorrendbe. Egész és lebegőpontos számok, valamint karakterláncok rendezésére is képes. Ha több paraméter is van, azok a tomb-hoz hasonlóan lesznek rendezve.

array uniq(array a)

E függvény a-ból eltávolítja az azonos elemeket, azokból csak egy példányt hagyva meg. Az eredményben az elemek sorrendje bármilyen lehet.

asszociatív tároló (mapping)

Speciális tömbök, melyekben tetszőleges típusú indexeket használhatunk, akár minden adathoz különböző típusút. Így nincs sorrend az adatok között. A tömbnél lassabb, és több memóriát használ, de van hozzá egy lookup függvény, amely "gyorsan" keres az adatok között.

Pl:

([ ]) // Üres mapping ([ 1:2 ]) // mapping egy index-érték párral ([ 1:({2.0}), "":([]), ]) // Különböző típusú értékek

Használható operátorok és függvények:

multihalmaz (multiset)

Hasonló a mappinghez, de nincsenek értékei, csak indexek. Indexeléskor igaz értéket kapunk vissza, ha van ilyen érték a halmazban, egyébként hamisat.

Értékadáskor (pl. mset [ ind ] = val), ha val=igaz, akkor az ind beletevődik a halmazba, ha nem igaz, akkor kiveszi belőle.

Pl:

(< >) //Üres multiset (< 17 >) // Multiset egy indexxel (< "", 1, 3.0, 1 >) // Multiset több értékkel

program

A Pike a program elnevezést használja az osztályokra. A Pike program egy class leírása fileban. Ha egy file-ból olvasunk be egy forrást, akkor a szintaktika a következő:

program p = compile_file("hello_world.pike");

Vagy cast-olással:

program p = (program) "hello_world";

Vagy, ha az interpreteren belül akarunk programot írni, akkor használhatjuk a class kulcsszót:

class class_name { inherits, variables and functions }

Pl:

class rekord { string cím; void create(string s) { cim = s; } }

Az tagváltozók objektum-szintűek, tehát minden, az osztályból létrehozott objektumban külön szerepelnek. A create függvény speciális, ez felel meg a C++-beli konstruktornak. Habár a Pike-ban van szemétgyűjtő, de definiálhatunk destruktort. Ezt destroy-nak kell neveznünk.

Az adattagokra és metódusokra alkalmazható módosítók:

objektum

A program konkrét megvalósítása. A klónozás (cloning) művelettel foglalunk le a program számára memóriahelyet, és így készítünk belőle objektumot. Az objektum a program egy példánya. Ez hasonlóan történik, mint C++-ban:

animal dog; dog = animal();

vagy paraméterezve(például az állat súlya):

animal dog=animal(50);

Az objektum metódusait és tagváltozóit a -> operátorral érhetjük el. pl: dog->weight.

Objektumokkal kapcsolatos függvények és operátorok:

Indexelés:

Az objektum karakterlánccal indexelhető, ily módon elérhetőek változói és függvényei. Ha változóról van szó, akár értéket is adhatunk neki ezzel a módszerrel. Függvény esetén az indexelés a rá mutató referenciát adja vissza, konstansoknak pedig az értékét. Megjegyzés: a -> operátor lényegében megegyezik az indexeléssel. Tehát az o->foo ugyanaz mint az o["foo"].

Klónozás:

Egy program klónozása kétféle módon történhet, vagy egy programra mutató referenciát függvényként meghívja, vagy pedig a new() illetve clone() függvények segítségével.

Egy objektum klónozásakor először a globális változók inicializálása történik meg, és utána meghívódik a create függvény a program argumentumaival paraméterezve.

void destruct(objektum o)

E függvény érvénytelenít minden, a paraméterbeli o objektumra mutató referenciát, valamint felszabadítja az objektum minden változóját. Ez a függvény hívódik meg akkor is, ha az objektumra mutató összes hivatkozás megszűnik. Ha van az objektumnak destroy függvénye, az végrehajtódik az objektum megszüntetése előtt.

program objektum_program(objektum o)

Visszatér a programmal, melyből a paraméterbeli o objektumot klónozták.

int objektump(mixed o)

1-el tér vissza, ha a paraméterbeli o objektum, 0-val különben. Ha az o objektum már megszűnt, a függvény 0-val tér vissza.

objektum this_objektum()

E függvény azzal az objektummal tér vissza, amelyet az interpreter éppen végrehajt.

array values(objektum o)

E függvény visszatérési értéke megegyezik a rows(o,indices(o))-éval, ami az o objektumban lévő azonosítók értékével tér vissza.

Összehasonlítás

Mint minden egyéb adattípusnál, itt is az == és a != használható két objektum egyenlőségének vizsgálatára.

Operátorok

Ahhoz, hogy megkönnyítsük a Pike-ban való progrmozást és hogy rövidebbé tegyük a kódot, néhány függvény meghívható a kódban 1-2 karakter leírásával. Ezeket a függvényeket operátoroknak hívjuk, és láthattuk már rengeteg példában hogyan is működnek. Ebben a fejezetben részletesen leírjuk mit is csinálnak. Az operátorokat kategóriákba soroljuk az alapján, hogy mire szolgálnak, de néhány operátornak olyan jelentése van, ami túlmegy a kategóriája hatáskörén.

Aritmetikai operátorok

Az aritmetikai operátorok a legegyszeűbbek, mivel úgy működnek, ahogy azt matematikában tanultuk. Ezek az aritmetikai operátorok:

Funkció Szintaxis Azonosító Jelentés
Összeadás a + b `+ a és b összege
Kivonás a - b `- a és b különbsége
Negáció - a `- (-1) * a
Szorzás a * b `* a szorozva b-vel
Osztás a / b `/ a osztva b-vel
Maradékolás a % b `% a maradéka a/b-nek

A harmadik oszlop, "Azonosító" a neve a függvénynek ami elvégzi a műveletet. Például a + b szintén írható +(a, b) alakban. A fejezet végén megmutatjuk, hogy milyen hasznos dolog is ez.

Mikor ezeket egész vagy valós számokra alkalmazzuk, akkor pontosan azt teszik, amit jelentenek. Egyedül a Maradékoló operátort nem ismerjük az általános matematikából. A maradékoló utasítás a maradékot adja vissza egy egész osztásból. Ez ugyanaz, mint az a - floor(a / b) * b. floor lekerekíti az értéket a legközelebbi kisebb vagy egyenlő egészre. Vegyük észre, hogy a floor hívása nem szükséges, ha egészekkel dolgozunk, mivel két egész hányadosa egész szám, ami a lekerekített érték. Például 8 / 3 értéke 2.

Ha minden argumentuma az operátornak egész, akkor az eredmény is egész lesz. Ha az egyik valós, a másik egész, akkor az eredmény valós értékű lesz. Ha mindkettő valós, akkor természetesen az eredmény is valós.

A Pike-ban több típus van, nem csak az egész és a valós, így itt a komplett lista a típusok kombinációjáról, amiket ezekkel az operátorokkal használhatunk:

Művelet Visszaadott típus Visszaadott érték
int + int
int a két érték összege
float + int
int + float
float + float
float a két érték összege
string + string
int + string
float + string
string + int
string + float
string Ebben az esetben a szám stringgé konvertálódik, majd a két string összefűződik egy új stringgé, amely visszaadódik.
array + array
array A két array konkatenálódik egy új tömbként, mely visszaadódik.
mapping + mapping
mapping Egy mapping adódik vissza az összes index-érték párral a két mappingból. Ha egy index mindkét mappingben szerepel, akkor a jobboldali mappingben lévő érték kerül az eredménybe.
multiset + multiset
multiset Egy multiset lesz az eredmény az összes indexszel mindkét multisetből.
int - int
int A jobboldali és a baloldali érték különbsége.
float - int
int - float
float - float
float A jobboldali és a baloldali érték különbsége.
string - string
string A baloldali string másolata a jobboldali string összes előfordulása nélkül.
array - array
array A baloldali tömb a jobb tömb béli értékek nélkül. Például:({2,1,4,5,3,6,7}) - ({3,5,1}) = ({2,4,6,7}).
mapping - mapping
mapping Egy új mapping az összes baloldali mapping index-érték párjával, kivéve azokat az indexeket, amelyek a jobboldaliban előfordulnak.
multiset - multiset
multiset A baloldali multiset értékei, kivéve amelyek előfordulnak a jobboldaliban.
- int
int Ugyanaz mint 0 - int.
- float
float Ugyanaz mint 0 - float.
int * int
int A két érték szorzata
float * int
int * float
float * float
float A két érték szorzata
array(string) * string
string A stringek a tömbben összefűződnek úgy, hogy minden elem közé a jobboldali string kerül. Példa: ({"foo","bar"})*"-" = "foo-bar".
array(array) * array
array Minden tömb a baloldali tömbből konkatenálódik úgy, hogy minden tömb közé a jobboldali tömb kerül. Példa: ({ ({"foo"}) ,({"bar"})})*({"-"}) = ({ "foo","-","bar" }).
string * int
string A stringet N-szer egymás után fűzi. Példa: "foo"*3 = "foofoofoo".
string * float
string A stringet X-szer egymás után fűzi. Példa: "foo"*2.5 = "foofoofo".
array * int
string A tömböt N-szer összefűzi. Példa:({"foo"})*3 = ({"foo","foo","foo"}).
array * float
string A tömböt X-szer összefűzi. Példa: ({1,2,3})*2.5 = ({1,2,3,1,2,3,1,2}).
int / int
int A bal- és a jobboldali érték hányadosának alsó egészrésze.
float / int
int / float
float / float
float A bal- és jobboldali érték hányadosa.
string / string
array(string) Szimmetrikusan a szorzás operátorral, az osztás feldarabolja a stringet. A jobboldali string előfordulásainál darabolódik fel a baloldali string. Példa: "foo-bar"/"-" = ({"foo","bar"})
string / int
array(string) A stringet N hosszúságú darabokra szedi, csak a teljes darabok lesznek az eredményben, a maradékot elhagyja. Példa: "foo-bar"/2 = ({"fo","o-","ba"})
string / float
array(string) Hasonló az egésszel osztáshoz, de megengedi a töredék méretű darabokat, és a maradékot is tartalmazza az eredmény. Példa: "foo-bar"/2.5 visszatér ({"fo","o-b","ar"})
array / int
array(array) Ugyanaz mint a string egésszel való osztása, csak tömbbel végzi a műveletet. Példa: ({1,2,3,4,5,6,7})/2 = ({({1,2}),({3,4}),({5,6})})
array / float
array(array) Ugyanaz mint a string float-tal való osztása, csak tömbbel végzi a műveletet. Példa:({1,2,3,4,5,6,7,8})/2 = ({({1,2}),({3,4,5}),({6,7}),({8})})
int % int
int Az osztás maradéka. Ha a és b egészek, akkor a%b ugyanaz, minta-(a/b)*b.
float % float
int % float
float % int
float Az osztás maradéka. Ha a és b egészek, akkor a%b ugyanaz, mint a-floor(a/b)*b
string % int
string A maradéka egy string osztásnak. Példa: "foo-bar"%2 = "r"
array % int
string A maradéka egy array osztásának. Példa: ({1,2,3,4,5,6,7})%2 = ({7})

Összehasonlító operátorok

Az aritmetika operátorok használata nehéz lenne anélkül, hogy összehasonlíthatnánk az eredményeket egymással. Erre a célra 6 operátor van:

Funkció Szintaxis Azonosító Visszaadott érték
Ugyanaz a == b `== 1 ha a értéke ugyanaz mint b, 0 különben
Nem ugyanaz a != b `!= 0 ha a értéke ugyanaz mint b, 1 különben
Nagyobb a > b `> 1 ha a nagyobb mint b, 0 különben
Nagyobb vagy egyenlő a >= b `>= 1 ha a nagyobb vagy egyenlő mint b, 0 különben
Kisebb a < b `< 1 ha a kisebb mint b, 0 különben
Kisebb vagy egyenlő a <= b `<= 1 ha a kisebb vagy egyenlő b, 0 különben

Az == és != operátorok bármely típussal használhatóak. Ahhoz, hogy 2 érték megegyezzen, ugyanolyan típusúnak kell lenniük, tehát 1 és 1.0 az nem ugyanaz. Szintén, hogy két pointer megegyezzen, a két értéknek ugyanarra az objektumra kell mutatni, az nem elég, hogy a két objektum ugyanolyan méretű és ugyanaz a tartalma.

A többi operátort a táblában csak integerekkel, floatokkal és stringekkel használhatjuk. Ha összehasonlítunk egy integer-t egy float-tal, akkor az integer float-tá konvertálódik az összehasonlítás előtt. Ha stringeket hasonlítunk, akkor a lexikai sorrend számít a LC_CTYPE és LC_LANG környezeti változóknak megfelelően.

Logikai operátorok

A logika operátorok azok, amelyek az igaz értékekkel operálnak. Pike-ban minden érték, kivéve a nulla, igaz érték. A logikai operátorok alapvető részei a Pike-nak. Ezek el tudják még dönteni azt is, hogy melyik argumentum értékelődjön ki és melyik ne. Mivel logikai operátoroknak nincs azonosítójuk, így függvényként sem lehet meghívni őket. Négy logikai operátor van:

Funkció Szintaxis Visszatérési érték
És a && b Ha a hamis, a adódik vissza és b nem lesz kiérékelve, különben b adódik vissza.
Vagy a || b Ha a igaz, akkor a adódik vissza és b nem kerül kiértékelésre, különben b adódik vissza.
Nem ! a 0 ha a igaz, 1 különben.
Ha-különben a ? b : c Ha a igaz, b adódik vissza és c nem értékelődik ki, különben c adódik vissza és b nem értékelődik ki.

Bitenkénti/halmaz operátorok

Ezek az operátorok bitek állítgatására használhatóak mint halmazok elemei. Szintén használhatóak tömbökre, mapping-ekre és halmazokra.

Funkció Szintaxis Azonosító Visszatérési érték
Léptetés balra a << b `<< Megszorozza a-t 2b-vel.
Léptetés jobbra a >> b `>> Osztja a-t 2b -vel.
Inverz (nem) ~ a `~ -1-a
Metszet (és) a & b `& Minden elem, ami van a-ban és b-ben is..
Unió (vagy) a | b `| Minden elem, ami vagy a-ban vagy b-ben van.
Szimmetrikus különbség (kizáró vagy) a ^ b `^ Minden elem, ami vagy a-ban vagy b-ben van, de nincs mindkettőben.

Az első három operátor csak integer-ekre alkalmazható nyilvánvalóan.

A másik három, a metszet, unió és szimmetrikus különbség alkalmazható integer-ekre, array-okra, multiset-ekre és mapping-ekre. Ha integer-ekkel használjuk, akkor minden bit a számban külön elemként szerepel.

Ha a metszetet, uniót és szimmetrikus különbséget array-el használjuk, akkor minden elem az array-ban önmagaként kezelődik. Tehát ha metszetét vesszük két array-nak, akkor az eredmény egy másik array lesz, mely tartalmazza azokat elemeket, mely mindkét arrayban megtalálhatóak. Pl.:({7,6,4,3,2,1}) & ({1, 23, 5, 4, 7}) = ({7,4,1}). Az elemek sorrendje a visszaadott array-ban mindig a baloldali array szerint lesz. Multiset-ek elemei ugyanúgy kezelődnek mint az array elemei. Ha mappingon végzünk műveletet, csak az indexek számítanak. Az értékek csak az indexekkel együtt másolódnak. Ha egy index a halmazművelet mindkét argumentumában megtalálható, akkor a jobboldali értéke fog számítani. Pl.: ([1:2]) | ([1:3]) = ([1:3]).

Indexelés

Az index és az intervallum operátorokat komplex adatszerkezetekből információ kinyerésére használjuk.

Funkció Szintaxis Azonosító Visszatérési érék
Index a [ b ] `[] Visszaadja a b indexű elemét.
Lookup a ->identifier `-> Megkeresi az azonosítót. Ugyanaz mint a["identifier"].
Index hozzárendelés a [ b ] = c `[]=; A b indexű elem értékét c-re állítja.
Index hozzárendelés a ->identifier = c `->= Az "identifier" indexű elem értékét c-re állítja.
Intervallum a [ b .. c ] `[..] Visszaadja az a szeletét b-től c indexig.
Intervallum a [ .. c ] `[..] Visszaadja az a szeletét az a elejétől b indexig.
Intervallum a [ b .. ] `[..] Visszaadja az a szeletét b indextől az a végéig

Az index operátort kétféleképpen írhatjuk le. Írhatjuk ob[ index ] vagy ob->identifier alakban, mivel az utóbbi ekvivalens az ob[ "identifier" ] alakkal.

Csak string, array, mapping, multiset és object indexelhető, és ezek különbözőképpen indexelhetőek, melyet a következő táblázat mutat:

Művelet Eredmény
string[int]
Visszaadja az ascii értékét az N-edik karakternek a stringben.
array[int]
Visszaadja az N-edik elemét az array-nak.
array[int]=mixed
Beállítja az N-edik elem értékét az array-ban a mixed értékre.
mapping[mixed]mapping->identifier
Visszaadja az értéket az adott indexről, 0 ha nincss ilyen index.
mapping[mixed]=mixed mapping->identifier=mixed
Az első mixed elem értéke a második mixed értéke lesz.
multiset[mixed]multiset->identifier
1-et ad vissza, ha az index (a zárójelben lévő érték) bennevan a multiset-ben, különben 0 az eredmény.
multiset[mixed]=mixed
multiset->identifier=mixed
Ha a mixed érték igaz, akkor az index hozzáadódik a multisethez, különben az index kivevődik a multisetből.
object[string]
object->identifier
Visszaadja az azonosítóhoz tartozó értéket az objektumban.
object[string]=mixed
object->identifier=mixed
Beállítja az objektumbeli azonosítóhoz tartozó értéket az adott mixed-re. Csak akkor működik, ha az azonosító referencia egy változóra az objektumban.
program[string]
program->identifier
Visszaadja a névkonstans értékét a programból.
string[int..int]
Visszaadja a string részstringjét.
array[int..int]
Visszaadja az array egy szeletét.

Ha array-t vagy string-et indexelünk, akkor néha kényelmesebb a végéről indexelni, mint az elejéről. Ezt a funkciót könnyen használhatjuk, ha negatív indexeket használunk, az arr[-i] az ugyanaz, mint arr[sizeof(arr)-i]. Fontos megjegyezni, hogy ez nem igaz az intervallumokra, ehelyett a intervallum(range) operátor szorítja meg az argumentumokat a megfelelő értékekre. Ez azt jelenti, hogy a[b..c] a következőt eredményezi:

Az értékadó operátorok

Igazából csak egyetlen értékadó operátor van, de kombinálható sok másik operátorral, ami a kódot rövidebbé teszi. Egy értékadás így néz ki:

változó = kifejezés;

A változó lehet lokális változó, globális változó vagy egy indexe egy array-nak, objektumnak vagy mapping-nek. Természetesen ez a változóban tárolt értéket kifejezésre fogja változtatni. Jegyezzük meg, hogy a fenti példa szintén egy kifejezés, melynek értéke a kifejezés. Ezt érdekes módokon használhatjuk fel:

variable1 = variable2 = 1; // Értékül adódik az 1 mindkét változónak variable1 =(variable2 = 1); // Ugyanaz, mint az előbb ... // Kiírja a kifejezés értékét, ha az bármi if(variable = expression) write(variable);

Az értékadás ilyen használata megtévesztheti a kezdő felhasználókat, vagy azokat akiknek Pascal vagy Basic hátterük van. Különösen az if utasítást keverhetik az if(variable == expression) alakkal, ami egy teljesen más dolgot jelent. Ahogy előzőekben már említettük az értékadó operátor más operátorokkal kombinálható, mely egy olyan operátort képez, amely nem csak simán értéket ad a változónak, hanem módosítja a tartalmát. Itt a teljes lista ezekről a kombinációkról:

Szintaxis Ugyanaz mint: Funkció
változó += kifejezés változó = változó + kifejezés Add a kifejezés-t a változó-hoz
változó -= kifejezés változó = változó - kifejezés változó-ból vond ki a kifejezés-t
változó *= kifejezés változó = változó * kifejezés Szorozd meg a változó kifejezéssel
változó /= kifejezés változó = változó / kifejezés Oszd el változó-t kifejezéssel
változó %= kifejezés változó = változó % kifejezés Maradékold a változót kifejezéssel
változó <<= kifejezés változó = változó << kifejezés Told el változó bitjeit kifejezéssel balra
változó >>= kifejezés változó = változó >> kifejezés Told el változó bitjeit kifejezéssel jobbra
változó |= kifejezés változó = változó | kifejezés változó-t VAGY-old a kifejezéssel
változó &= kifejezés változó = változó & kifejezés változó-t ÉS-eld kifejezéssel
változó ^= kifejezés változó = változó ^ kifejezés változó XOR-old kifejezéssel

Ezekben a kifejezésekben a változó bármilyen típusú lehet, aminek lehet értéket adni. Ezeket balértékként ismerjük. Ezek a balértékek:

Balérték típus Szintaxis Érvényes értékadás
lokális vagy globális változó azonosító ugyanaz, mint a változó
array egy eleme array [ int ] bármilyen típus
elements in elements in an array array [ string ] any type This is like map(arr, `[]=,string_indexing_element, assignment_element)
egy elem a stringben string [ int ] integer
egy elem egy mapping-ban mapping[mixed] or mapping->identifier bármilyen típus
egy elem egy multiset-ben multiset[mixed] or multiset->identifier igaz / hamis
változó egy objektumban object[string] or object->identifier ugyanaz a típus mint a változó
balértékek listája [ lvalue, lvalue ] egy tömb, első elemnek a tömbben az első érték, a másodiknak a második érték adódik értékül (és így tovább).

Az értékadó operátorok

Már csak néhány operátor maradt hátra. Ezeket most ebben a csoportban mutatjuk be, nem azért mert nem fontosak, csak nem illenek bele egyik másik csoportba sem.

Funkció Szintaxis Azonosító Visszatérési érték
Hívás
a ( args ) `() Meghívja az a függvényt.
splice @ a nincs
A tömb minden egyes elemével meghívja a függvényt.
Növelés
++ a nincs
Növeli a-t és visszaadja az új értékét.
Csökkentés
-- a nincs Csökkenti a-t és visszaadja az új értékét.
Utólagos növelés a ++ nincs Növeli a-t és visszaadja az régi értékét.
Utólagos csökkentés a -- nincs Csökkenti a-t és visszaadja az régi értékét.
casting
(type) a nincs Megpróbálja a értékét type típusra alakítani.

a, b nincs Kiértékeli a-t és b-t, majd visszaadja b-t.

A legfontosabb ezen operátorok közül a hívás operátor. Az operátor maga csak egy pár zárójel a kifejezés után írva megadja a függvényt. Az argumentumokat a zárójelek között kell megadni vesszővel elválasztva. Már láthattunk sok példát erre az operátorra, bár akkor még nem tudhattuk, hogy ez egy operátor. A függvényhívás operátor többet tud, mint szimplán meghív egy függvényt: a "függvény" lehet egy tömb, az operátor pedig ezen a tömbön végighaladva meghív sorban minden egyes elemet, és visszaad egy tömböt az eredményekkel.

Másrészről a "függvény" egy program, az operátor klónoz egy objektumot a programból és meghívja a create() metódust az új objektumban az adott argumentumokkal. Tehát a clone így van implementálva:

object clone(mixed p, mixed ... args) { ( (program)p )(@args); }

A függvényhívásoknál meg kell említeni a splice operátort is. A splice operátor egy kukac a kifejezés előtt. A kifejezés mindig egy tömb. A splice operátor a tömb minden egyes elemét külön átadja a függvényhívásnak. Az operátort csak argumentum listában, függvényhívásnál használhatjuk.

Itt vannak a növelő és csökkentő operátorok. Ezek valamelyest korlátozottak: csak integer-eken lehet használni őket. Gyorsan és egyszerűen használhatóak az 1 kivonására és hozzáadására egy integerhez. Ha az operátor a változó előtt van (++a), a visszaadott érték a változó a művelet előtti értéke lesz, ha a változó után van írva, akkor a művelet utáni érték adódik vissza.

A casting-ot az egyik típus másik típussá konvertálásakor alkalmazzuk, de nem minden konverzió lehetséges. A lehetséges konverziók, amik csinálnak valamit:

konvertálás erről erre
művelet
int string Átkonvertálja az int-et az ASCII reprezentációjára.
float string Átkonvertálja az float-ot az ASCII reprezentációjára.
string int Átkonvertálja a decimális, oktális vagy hexadecimális számot egy int-té. A jövőbeni verziókban csak a decimális konverzió fog működni.
string float Az ASCII számot float-tá konvertálja.
string program A string egy filenév, a program lefordul és a program visszaadódik. Az eredmény cache-elődik.
string object Először konvertája a stringet programmá, (lásd előbb), majd klónozza az eredményt. Az eredmény cache-elődik.
object type Meghívja a 'cast' függvényt egy stringgel, melynek tartalma a type.
string array Ugyanaz mint values(string).
array(int) string Ez az ellenkezőjét csinálja az előzőnek, tehát ez egy stringet készít egy integer array-ból.
array array(type) Rekurzívan konvertálja a tömb összes elemét
mapping array Ugyanaz mint Array.transpose(({indices(mapping),values( mapping)).
Pl.: (array)([1:2,3:4]) = ({ ({1,2}), ({3,4}) })
multiset array Ugyanaz mint indices(multiset).
int float int értékű float-ot ad vissza.
float int A float-hoz legközelebbi integer-t adja vissza.
function object Ugyanaz mint function_object(function).

A cast operátor használhatjuk még arra is, hogy a fordítónak megmondjunk dolgokat. Ha a egy mixed típusú változó int típusú értékkel, akkor az (int)a használható az a kifejezés helyett hogy a fordítóval közöljük, hogy a kifejezés értéke int.

És utoljára, de nem utolsó sorban a vessző operátor. Ez nem sok mindent csinál, szimplán kiértékeli a két argumentumát, majd visszaadja a jobboldali értékét. Ez az operátor legfőképpen arra szolgál, hogy rövidebb kódot írhassunk.

Operátor precedencia

Ha egy kifejezést értékeltetünk ki, akkor mindig használhatunk zárójelet, hogy megmondjuk a fordítónak milyen sorrendben történjen. Alapesetben a fordító balról jobbra halad, de a magasabb precedenciájúakat előbb végzi el, mint az alacsonyabbakat. A következő táblázat a operátorok precedenciáját sorolja fel csökkenő sorrendben:

(a) a() a[b] a->b a[b..c] ({}) ([]) (<>)
!a ~a (type)a ++a --a
a++ a--
a*b a/b a%b
a+b a-b
a>>b a<<b
a>b a>=b a<b a<=b
a==b a!=b
a&b
a^b
a|b
&&
||
a?b:c
=
@a
,

Példák:

Kifejezés
a sorrend, ahogy kiértékelődik:
1+2*2 1+(2*2)
1+2*2*4 1+((2*2)*4)
(1+2)*2*4 ((1+2)*2)*4
1+4,c=2|3+5 (1+4),(c=((2|3)+5))
1+5 & 4 == 3 (1+(5 & 4)) == 3
c=1,99 (c=1),99
!a++ + ~--a() (!(a++)) + (~((--a)()))

Operátor függvények

Ahogy korábban említettük a + b írható `+(a, b) alakban is. Együtt a map függvénnyel, mely meghív egy függvényt minden indexére egy array-nek, és a splice operátorral nagyon-nagyon gyors és kompakt kódot írhatunk. Nézzünk néhány példát:

map(arr, `-)

Ez egy tömböt eredményez, ahol minden elem az eredeti negáltja lesz.

map(text/"\n",`/," ")

Ez a szöveget sorokra bontja, minden sor szavak listájára osztja a `/ -vel.

`+(0, @arr)

Ez az összes integert összeadja az arr array-ben.

int abs(int a) { return ( a>0 ? `+ : `-)(a); }

Ez egy kicsit abszurd, de működő függvény, ami az a abszolútértékét adja vissza.