A q programozási nyelv

Típusok, típuskonstrukciók

Elemi típusok

Egész típusok

Az int

Az int egy előjeles, négy bájtos egész szám típus.

42

Short és long

A másik két egész számtípus a short és a long. A short típus egy kétbájtos, előjeles egész szám, a short literált egy lezáró 'h' karakter jelzi. Például:

-123h

Hasonlóan, a long típus megfelel egy nyolcbájtos előjeles egész számnak, a long literált pedig egy lezáró 'j' karakter jelez:

1234567890j

Legebőpontos számok

A float

A float típus az IEEE szabványnak megfelelő nyolcbájtos lebegőpontos szám, amit gyakran "double"-nek hívnak más programozási nyelvekben. Egy float literál egy opcionális előjel-karakterrel kezdődik, amelyet számjegyek követnek, található benne egy tizedespont, a végén pedig opcionálisan egy lezáró 'f' található:

3.14159265 1f
A real

A real típus egy négybájtos lebegőpontos szám. A real literálok jelölése számjegyekből, tizedespontból, és egy lezáró 'e' karakterből áll. Ezt a típust hívják 'float'-nak néhány másik programozási nyelvben.

1.4142e

Bináris típusok

Bit- és bájtértékek használatához.

Boolean

A boolean típus egy bájt méretű, és egy bitnyi adatot tárol. Jelölése ennek a bitnek az értéke, lezárva egy 'b' karakterrel:

0b
Byte

A byte típus egy bájtot foglal, és 8 bitnyi adatot tárol. A jelölése '0x'-szel kezdődik, és egy vagy két hexadecimális számjegy követi:

0x2a
A bináris adat is szám

A bináris adatok kezelésében a q jobban hasonlít a C-re, mint annak saját leszármazottai hasonlítanak rá. Ugyanis a bináris típusokat előjel nélküli egészeknek vehetjük, és részt vehetnek matematikai kifejezésekben és összehasonlításokban más szám típusokkal. Nincs 'true' és 'false' kulcsszó, valamint nincsenek külön logikai operátorok se.

a:42 bit:1b a+bit 43
byte:0x2a pi:3.14159265 byte+pi 45.14159

Karakter típusok

Két elemi karaktertípus van q-ban. Ezek a típusok jobban megfelelnek az SQL-beli CHAR és VARCHAR típusoknak, mint más programozási nyelvek karakter típusai.

A char

Egy char egy darab ASCII karakternek felel meg, és egy bájton tárolódik. Ez megfelel az SQL-es CHAR-nak. A char literált egy karakterrel jelöljük, és idézőjelek veszik körül:

ch:"q"

Néhány karaktert, csakúgy mint az idézőjelet, nem lehet rendesen megadni, hiszen speciális jelentéssel bírnak. Akárcsak C-ben, ezeket a karaktereket escape-elni kell egy '\' karakterrel.

ch:"\"" / double-quote ch / console also displays the escape "\"" ch:"\\" / back-slash ch:"\n" / newline ch:"\r" / return ch:"\t" / horizontal tab
A symbol

A symbol karakterek szekvenciáját tárolja. Egy symbol literált egy '`' (visszafordított idézőjel (AltGr+7 magyar billentyűzeten)) karakter kezd. Q-körökben ezt a karaktert "back tick"-nek hívják.

s1:`q s2:`zaphod

A symbol felbonthatatlan, ami azt jelenti hogy az egyes karakterek amik alkotják, direkt módon nem hozzáférhetők.

Fontos: A symbol nem string. Látni fogjuk később, hogy van egy string-ekhez hasonló konstrukció q-ban, mégpedig a karakterekből álló lista. Bár egy char-lista közeli rokona a symbolnak, hangsúlyozzuk, hogy a symbol nem char-okból épül fel. Az `a symbol és az "a" karakter nem ugyanaz. A "q" karakter és a `kdb symbol mindketten elemi típusok elemi értékei!

Dátum és idő típusok

Egyik nagy előnye a q-nak, hogy képes mind idősoros, mind relációs adatokat feldolgozni konzisztens és hatékony módon. Q kiterjeszti az alapvető SQL dátum és idő típusokat, hogy megkönnyítse az időbeli adatokon való műveletvégzést, amire SQL-ben minimális lehetőségünk van, más programozási nyelvekben pedig eléggé ügyetlenül van megoldva. Azokkal kezdünk, amelyek SQL-ben is jelen vannak, a többi típus q-ban az idő kisebb részegységeivel foglalkoznak (pl. hónap, perc, másodperc).

A date

A date négy bájton van tárolva, a jelölése pedig yyyy.mm.dd, ahol yyyy felel meg az évnek, mm a hónapnak, dd pedig a napnak. A date értéke az eltelt napok számát tárolja 2000 január 1.-je óta.

d:2006.07.04

A kezdő nullák a hónapoknál és napoknál szükségesek, elhagyásuk hibának számít.

bday:2006.7.4 / error!
Time

A time típus négy bájton tárolódik, a jelölése pedig hh:mm:ss:uuu, ahol hh az óra (01-23), mm a perc, ss a másodperc, uuu pedig a milliszekundum. A time érték az eltelt milliszekundumokat tárolja éjféltől számítva.

t:09:04:59.000

A kezdőnullák itt is minden időegységnél szükségesek!

Datetime

A datetime a date és time típusok kombinációja, szeparálva egy 'T'-vel, az ISO szabvány szerint.

dt:2006.07.04T09:04:59:000
Month

A month típus négybájtos, a jelölése pedig yyyy.mm, egy lezáró 'm'-el a végén. A month érték az elmúlt hónapokat tárolja 2000 januárjától.

mon:2006.07m
Minute

A minute típus négy bájtos, a jelölése pedig hh:mm. A minute érték az eltelt perceket tárolja éjféltől számítva.

mm:09:04
Second

A second típus négybájtos, a jelölése pedig hh:mm:ss. A second értéke az elmúlt másodperceket tartalmazza éjféltől.

sec:09:04:59

Listák

Összetettebb adatok az elemi típusokból (atomok) épülnek fel, és listákból. Fontos, hogy alaposan megismerjük a listákat, hiszen szinte az összes q program magába foglalja listák feldolgozását. A koncepció egyszerű, de a bonyolultság gyorsan nőhet.

A lista

A lista egyszerűen egy rendezett adatgyűjtemény. Pontosabban, egy lista az egy rendezett gyűjteménye az elemi típusoknak, és más listáknak. Mivel ez a definíció rekurzív, kezdjük az elemi típusok listáival.

Lista definíció és értékadás

Egy általános listának a jelölése zárójelben történik, az elemeit felsorolva, pontosvesszővel elválasztva. Az olvashatóság kedvéért opcionálisan whitespace karaktereket használhatunk a pontosvesszők után.

(1;2;3) ("a";"b";"c";"d") (`Life;`the;`Universe;`and;`Everything) (-10.0; 3.1415e; 1b; `abc; "z")
Az fenti példában az első három lista egyszerű, ami azt jelenti, hogy a lista elemei atomok, és megegyezik a típusuk. Az utolsó lista egy általános lista (general list), ami azt jelenti, hogy nem egyszerű. Más szavakkal egy általános lista tartalmazhat nem atomi elemeket, vagy atomi elemeket különböző típussal, beágyazott listákat egységes típussal, esetleg beágyazott listákat különböző típusokkal.

Fontos: A definíció az elemek között sorrendet határoz meg (balról jobbra). Az (1;2) lista és a (2;1) lista különbözőek. Az SQL halmazokra alapul, amelyek így rendezetlenek. Ez a differencia további különbségekhez vezethet lekérdezéseknél q és SQL között. A listák rendezettsége egyszerűvé és hatékonnyá teszi az idősoros adatok feldolgozását q-ban, ami nehézkes és igen kevéssé hatékony SQL-ben.

A listákat értékül adhatjuk változóknak, akárcsak az elemi típusokat:

L1:(1;2;3) L2:("z";"a";"p";"h";"o";"d") L3:(`Life;`the;`Universe;`and;`Everything) L4:(0b;1b;0b;1b;1b;0b) L5:(-10.0;3.1415e;1b;`abc;"z")

Elemszámlálás

A lista elemszáma könnyen hozzáférhető a count függvénnyel:

count L1 3

A count egy int értékkel tér vissza, ami megegyezik a jobb oldalán lévő lista elemszámával:

count (-10.0;3.1415e;1b;`abc;"z") 5

Egy atom elemszáma mindig 1:

count `abcd 1

Egyszerű lista

Egy egyszerű lista - ami egy egységes típusú, atomokból álló lista - megfelel a matematikában használatos vektornak (innentől a vektor szinonimája az egyszerű listának). Az ilyen listák speciálisan vannak kezelve q-ban. Egyszerűbb a jelölésük, kevesebb helyre van szükségük tároláskor, és gyorsabban lehet rajtuk műveleteket végezni, mint általános listákon. A jelölésük történhet úgy, mint az általános listáké, de ahol lehetséges, a q az általános listákat vektorrá konvertálja.

Egyszerű, egész típusú listák

Bármelyik numerikus típusból álló egyszerű lista jelöléséből kihagyhatjuk a zárójeleket és pontosvesszőket. Így tehát a következő két definíció ekvivalens:

(100;200;300) 100 200 300

Egyszerű, lebegőpontos listák

Hasonlóan jelöljük a real-ek és float-ok egyszerű listáit. Vegyük észre, hogy a q konzol elhagyja a tizedespontot, amikor attól jobbra csak nullák állnának, ám az érték ettől még nem lesz int:

F:(123.4567;9876.543;99.0) F 123.4567 9876.543 99

Hogyha egyik értéknek sincs a tizedesponttól jobbra számjegye:

FF:1.0 2.0 3.0 FF 1 2 3f

Vegyük észre a lezáró 'f'-et a lista végén, ez jelöli, hogy float-okból áll a lista.

Egyszerű bináris típusú listák

Az egyszerűbb jelölése a bináis típusú listáknak szimplán egymás mellé helyezi az értékeket, majd lezárja a típust jelző karakterrel:

bits:(0b;1b;0b;1b;1b) bits 01011b

Byte-okból listáknál a típust jelző 0x a lista elején található:

bytes:(0x20;0xa1;0xff) bytes 0x20a1ff

Megjegyzés: A bitekből álló lista annyi bájtból áll, ahágy eleme van. A q nem sűrűti össze a bitekből álló listákat. A fenti bits lista öt bájt helyet foglal ennek megfelelően.

Egyszerű symbol-lista

Az egyszerűsített jelölése a symbol listáknak elhagyja a zárójeleket és a pontosvesszőket, valamint nem lehet közöttük whitespace sem:

good:`This`is`correct bad:`Ths `is `wrong
Egyszerű karakter-lista

Az egyszerűbb jelölés a karakter-listáknak pontosan úgy néz ki, mint egy string a többi programozási nyelvben:

"so long"

Egyszerű jelölések összesítése
L:100 200 300 H:1 2 255h F:123.4567 9876.543 99.99 bits:01011b bytes:0x20a1ff symbols:`Life`the`Universe`and`Everything chars:"so long"
Dátum és idő típusú adatokból álló lista

Különböző dátumtípusokkal definiált lista más viselkedéssel jár, mint egy különböző numerikus típusokból álló lista. Ebben az esetben a lista első elemének a típusát vesszük alapul, a többit pedig szűkítjük/bővítjük, hogy megfeleljen ennek.

12:34 01:02:03 12:34: 01:02
01:02:03 12:34 01:02:03 12:34:00

Ahhoz, hogy kierőszakoljuk egy vegyes típusú listát dátum és idő típusokból, jelölnünk kell a típusazonosító karaktert:

01:02:03 12:34 11:59:59.999u 01:02 12:34 11:59

Üres és Singleton Listák

Egyelemű és üres listák különleges elbánásban részesülnek.

Az általános üres lista

Hasznos, hogy legyen egy üres listánk. Egy pár üres zárójel (maximum whitespace-t tartalmazhat) jelöli az üres listát:

L:( ) L (nincs output)
Egyelemű lista

Érdekes, ahogyan a q az egyelemű listákat kezeli. Mellesleg az ilyen listákat q-ban singleton-nak hívjuk. Egy singleton létrehozása egy jelölési problémával jár. Először is, egy lista amely csupán egyetlen atomot tartalmaz, különbözik egy atomtól. Hiszen ha van egy dolog egy dobozban, az nem ugyanaz mint a dolog a doboz nélkül. A következőket atomokként ismerjük fel:

42 1b 0x2a `beeblebrox "z"

A következőket pedig kételemű listaként:

42 6 01b `zaphod`beeblebrox "zb" (40;`two)

Hogyan hozzunk létre listát, ami mindössze egyetlen elemet tartalmaz? Jó kérdés. A válasz pedig, hogy nincs szintaktikus módszer erre. Talán arra gondolhatunk, hogy pusztán egy darab elemet zárójelbe írunk, de ez nem működik, mivel az eredmény így is egy atom:

singleton:(42) singleton 42

Az egyelemű listák készítésének a módja, hogy használjuk az enlist nevű függvényt, ami egy singleton listával tér vissza, ami azt az egy elemet tartalmazza, amit az enlist-nek megadtunk:

singleton: enlist 42 singleton ,42

Megjegyzés: az outputban látható vessző a q magnyelvének, a k-nak egy operátora. q kifejezésben ezt nem használhatjuk, ezért van helyette az enlist függvény.

A következő kifejezés szintén egy singleton listát hoz létre:

singleton:enlist 1 2 3 count singleton 1

Indexelés

A lista rendezve van, mégpedig balról jobbra. A lista első elemét 0-val indexeljük, a másodikat (ha van) 1-gyel, és így tovább. Egy n-elemű lista elemei 0-tól n-1-ig vannak indexelve.

Jelölése

Adott egy L nevű lista, az i-edik elemhez az L[i] kifejezéssel férhetünk hozzá:

L:(-10.0;3.1415e;1b;`abc;"z") L[0] -10f
L[1] 3.1415e
Értékadás indexen keresztül

Egy lista egy elemének értéket is adhatunk az indexén keresztül:

L1:1 2 3 L1[2]:42 L1 1 2 42

Fontos: Egyszerű listáknál (vektor) indexen keresztüli értékadásnál a típusoknak pontosan meg kell egyezniük. Továbbá ha egy általános listában értékadások miatt minden elem ugyanolyan típusuvá válik, a lista "leegyszerűsödik" és ezután nem tudunk bele más típusú elemet rakni:

L1:(1;2;3f) L1[2]:42 L1 1 2 42 L1[2]:42f 'type
Indextartomány

Ha rossz típussal próbálunk indexelni, hibát kapunk:

L:(-10.0;3.1415e;1b;`abc;"z") L[`1] 'type

Ha olyan indexet adunk meg, amelyik a lista határán kívül van, akkor az eredmény nem lesz error. Egy null-értéket kapunk vissza. Ha a lista egyszerű, akkor ez a null érték a listában lévő atomhoz tartozó null. Általános listák null-értéke 0n.

L[5] 0n

Üres index és null-elem

Egy üres index visszaadja az egész listát:

L[] -10f 3.1415e 1b `abc "z"

A dupla kettőspont (::) jelöli a null-elemet, ami explicit jelölést ad az üres indexre:

L[::] -10f 3.1415e 1b `abc "z"

Megjegyzés: A null-elem típusa nem meghatározott (undefined), a típusa nem felel meg egyik normális elemnek sem a listában. Ezért a null-elemmel kierőszakolhatjuk általános lista létrehozását:

L:(1;2;3;::) L 1 2 3 ::
type L 0h
Változókból álló lista

Listákat változókból is létrehozhatunk:

L1:(1;2;100 200) L2:(1 2 3;‘ab`c) L6:(L1;L2) L6 1 2 100 200 1 2 3 `ab `c

Listák összefűzése

Listák összefűzése a vessző (,) operátorral történik. A vessző operátor jobb oldalán lévő listát hozzáfűzi a vessző bal oldalán lévő listának a végéhez:

1 2,3 4 5 1 2 3 4 5
Ha az argumentumok típusa nem egyezik meg, akkor általános listát kapunk.

Listák egymásba ágyazása

Listákat egymásba ágyazhatunk egyszerűen úgy, hogy egy listának elemként egy másik listát adunk meg

Mélység

Mélységen az egymásba ágyazott listák szintszámát értjük. Az atomok mélységeit 0-nak vesszük, az egyszerű listák mélysége pedig 1.

Az összetett listák jelölése is mutatja az egymásba ágyazásokat. A következő listának 2-es a mélysége, 3 eleme van, az első kettő atom, az utolsó pedig egy egyszerű lista:

L1: (1;2;(100;200)) count L1 3

A belső listát egyszerűbb alakban is írhatjuk:

L1: (1;2;100 200) L1 1 2 100 200
Egymásba ágyazott listák indexelése

Egymásba ágyazott listák indexelésénél is a [] operátort használjuk, méghozzá a C-hez hasonló módon:

L:(1;(100;200;(1000;2000;3000;4000))) L[1][2] 1000 2000 3000 4000
L[1][2][0] 1000

Ezenkívül van még egy alternatív jelölés az egymásba ágyazott listák indexelésére. Itt az indexeket pontosvesszővel elválasztva adhatjuk meg:

L[1;2;0] 1000
Multi-indexelés

Q-ban lehetőség van egy listának egyszerre több elemének is a lekérdezésére:

L1:100 200 300 400 L1[0 2] 100 300

Az indexek lehetnek bármilyen sorrendben:

L1[3 2 0 1] 400 300 100 200

Egy index többször is szerepelhet:

L1[0 2 0] 100 300 100

Ez megmagyarázza, hogy miért kell pontosvesszőket beiktatnunk, amikor mélységekben indexelünk.

Indexelés egyszerű listákkal

Indexnek használhatunk akár egy másik listát is:

I:3 2 0 L1[I] 400 300 100
Indexelés általános listákkal

Már az egyszerű listákkal történő indexelés is bemutatja, hogy milyen kifejezőereje és flexibilitása van a q-nak, de ettől még mélyebb vizekre is evezhetünk. Amikor egyszerű listákkal indexeltünk, akkor az eredmény lista egy új lista volt, amelynek az értékei az eredeti lista első szintjéről származtak, a lista alakja pedig megegyezett magával az indexszel. Igaz ami igaz, az eredmény listának az alakja meg kell hogy egyezzen az index-listának az alakjával. Ez megmagyarázza mi történik, mikor nem egyszerű listával indexelünk:

L1:100 200 300 400 L1[(0 1; 2 3)] 100 200 300 400
I:(1;(0;(3 2))) L1[I] 200 (100;400 300)

A listákról és indexelésükről további információk: itt.

Típuskényszerítés

A típuskényszerítés (cast) az a művelet, amellyel egy értéknek megváltoztathatjuk a típusát. A q ezt egy bináris operátor, a $ segítségével oldja meg. Az operátor első paramétere a típus, amelyre konvertálni akarunk, a második pedig a konvertálandó érték. A típust megadhatjuk háromféleképpen: vagy a nevét adjuk meg szimbólumként (pl. `long), vagy a számkódját short típusban (pl. 7h - vigyázat, más számtípussal nem működik), vagy a karakterkódját (pl. "j").

`char$0x42 "B" 6h$4.2 4 "x"$42 0x2a

Ez a módszer nem működik szimbólumok és stringek esetén. Egy atomi értékből stringet csinálhatunk a string nevű függvénnyel:

string 0x42 "42" string `alma "alma"

Stringből szimbólumot képezhetünk úgy, hogy az üres szimbólumot használjuk típusnév helyett. Így létrehozhatunk pl. szóközt és ékezetes betűket tartalmazó szimbólumot is:

`$"szóköz is van benne" `szóköz is van benne

Végül stringből kiolvashatunk számot és egyéb típusokat is. Ehhez a karakteres típusnevet kell használnunk, csak nagybetűsen:

"J"$"42" 42j "I"$"nem szám" 0N

A típuskényszerítés akkor is hasznos lehet, ha egy egyszerű listába szeretnénk betenni egy olyan elemet, amelynek nem ismerjük a típusát. A type függvény egy q objektumról visszaadja típusának kódját. Atomi típus esetén negatív előjellel, egyszerű lista esetén pozitív előjellel kapjuk meg a típust. A kapott értéket felhasználhatjuk a $ operátorban is:

c: 10 20 30 40 c[1]:42h 'type c[1]:(type c)$42h c 10 42 30 40

Szótárak

A szótár tulajdonképpen a lista általánosítása, és az alapja a tábláknak. A szótár egy hozzárendelés egy értelmezési tartomány-lista (továbbiakban domain-lista) és egy értéktartomány-lista (továbbiakban érték-lista) között. A két listának megegyező elemszámúnak kell lennie, és a domain-lista elemeinek páronként egyedinek illene lenniük, bár ez nem kötelező q-ban.

Alapok

A szótár egy rendezett gyűjtemény kulcs-érték pároknak. Minden szótárnak 99h a típusa. Szótárakat a felkiáltójel (!) operátorral hozhatunk létre. A count függvénnyel pedig megkaphatjuk a szótárban lévő bejegyzések számát:

d1:`Dent`Beeblebrox`Prefect!98 42 126 count d 3

Egy szótár kulcsait megkaphatjuk a key függvénnyel:

key d `Dent`Beeblebrox`Prefect

Egy szótár értékeit megkaphatjuk a value függvénnyel:

value d 98 42 126

A konzol a szótárat a következő formában jeleníti meg:

d Dent | 98 Beeblebrox | 42 Prefect | 126

A cols függvény szintén visszaadja a domain-listát:

cols d `Dent`Beeblebrox`Prefect

Megjegyzés: A szótárban lévő bejegyzések sorrendje számít, csakúgy mint a listák elemeinek a sorrendje is számít. Így aztán két olyan szótár, ami ugyanazokat a bejegyzéseket tartalmazza, viszont más sorrendben, nem egyenlőek egymással:

d1:`Prefect`Beeblebrox`Dent!126 42 98 d~d1 0b

Keresés

Egy adott kulcshoz tartozó értéket könnyedén visszakaphatunk egy szótárból. A szintaxis hasonló a listák indexeléséhez:

d[`Beeblebrox] 42
d `Beeblebrox 42

Csakúgy mint listák esetében, ha olyan kulccsal keresünk, amihez nincs bejegyzés a szótárban, akkor az eredmény nem error, hanem egy típusnak megfelelő null-elem:

d[`Slartibartfast] 0N

Szintén működik a listáknál látott multi-indexelés is:

d[`Dent`Prefect] 98 126
Szótár kontra Lista

A szótár a listának egy általánosítása, ahol az elemek indexelésének lehetőség kiterjesztettük. Másrészt, egy szótárat nem indexelhetünk pozíció szerint, hogy visszakapjunk egy egész kulcs => érték bejegyzést. Az ilyen próbálkozás hibával jár.

d:"abcde"!1.1 2.2 3.3 4.4 6.5 d["c"] 3.3
d[0] 'type

Persze természetesen készíthetünk olyan listát, aminél a hozzárendelés emulálja a listák indexelését:

L3:`one`two`three L[1] `two
d3:0 1 2!`one`two`three d[1] `two

Amikor ezesetben megkérjük a q-t, hogy hasonlítsa össze a két entitás egyenlőségét, akkor ő pozíció alapú indexelést használ. Ezután ellenőrzi egyesével az elemeket:

L3=d3
0| 1 1| 1 2| 1

A fenti kimenet azt jelenti, hogy a 0., 1. és 2. elem is megegyezik. Persze az így definiált szótár nem ugyanaz mint a lista:

L3~d3 0b

Habár az elemek elérése egy "lista-stílusú" szótárban jelölésben azonos a listák elem-indexeléséhez, a kettő nem ugyanaz. Az elem-indexelés egy pozícionális eltolás, míg a szótárban az elem elérése egy keresés. Különbözőképpen vannak implementálva a felszín alatt.

Kulcsok egyedisége

Ahogy korábban megjegyeztük, a q nem kényszeríti ki azt, hogy a szótárak domain-listája páronként különböző elemekből álljon. Hogyha egy kulcs többször is szerepel, akkor a keresés csupán egy elemet fog visszaadni, mégpedig az elsőt az érték-listából:

ddup:8 4 8 2 3 1!`one`two`three`four`five`six ddup[8] `one

Megjegyzés: A fordított visszakeresés megfelelően működik egy nem egyedi domain-listán:

ddup?`three 8
Nem egyszerű domain- vagy érték-lista

Nem kötelező, hogy a szótár érték-listája atomokból álljon. Lehet akár egy általános lista is, ami beágyazott listákat tartalmaz:

dgv:(1;2h;3.3;"4")!(`one;2 3;"456";(7;8 9)) dgv["4"] 7 8 9
A kulcsoknak sem kell atomoknak lenniük:
dgk:(0 1; 2 3)!`first`second dgk[0 1]
`first
dgk[2 3]
`second

Megjegyzés: Ha a domain-lista elemeinek az alakja különbözik, akkor a keresés nem működik megfelelően:

dweird:(0 1; 2; 3)!`first`second`third dweird[0 1] `first
dweird[2] `
dweird[3] `

A megfigyelt viselkedés azt mutatja, hogy a kulcs általi keresés csődöt mond az első olyan kulcsnál, amelyik alakja különbözik a legelső kulcstól.

Műveletek

Módosítás és upsert

Ahogy listáknál, az elemek a szótárban is módosíthatóak index általi értékadással:

d:10 20 30!"abc" d[30]:"x" d 10| a 20| b 30| x

Fontos: A listákkal ellentétben, a szótárak bővíthetőek index általi értékadással:

d[40]:"y" d 10| a 20| b 30| x 40| y
L:"abc" L[3]:"x" 'length

Tehát index általi értékadásnál, ha már van ilyen kulcsunk, akkor update-elünk, különben insert-elünk a szótárba. Ezt röviden upsert-nek hívjuk. Mivel a táblák a könyvtárakra épülnek, ez az upsert viselkedés a táblákra is igaz lesz.

Fordított keresés

Listáknál a kérdőjel (?) operátort használtuk arra, hogy megkeressük egy adott érték indexét. Ez a módszer a könyvtárakkal is működik:

d:`a`b`c!1001 1002 1003 d?1002 `b

Ha olyan elemet keresünk, amelyik nincs az érték-listában, akkor az eredmény egy típusnak megfelelő null-érték lesz. Egyszerű listáknál ez megfelel a lista típusához tartozó null-értéknek. Általános listáknál a null-érték 0N.

Bejegyzés törlése

A bináris törlő operátor (_) eredménye egy olyan szótár, melyből töröltük a megadott kulcshoz tartozó bejegyzést.

Megjegyzés: Whitespace szükséges a _ baloldalára, ha a baloldali operandus egy változó:
d:1 2 3!`a`b`c d _2 1| a 3| c

Ha több elemet akarunk törölni egyszerre, akkor szintén az aláhúzás (_) operátort kell használnunk, ám ez esetben a jobboldali operandus a szótár, a baloldali pedig egy lista, mely a törlendő kulcsokat tartalmazza:

d:1 2 3!`a`b`c (enlist 2)_d 1| a 3| c
1 3_d 2| b
(enlist 42)_d 1| a 2| b 3| c
Ha olyan elemeket akarunk törölni, amihez nem tartozik bejegyzés a szótárban, nem történik semmi.

A szótárakról bővebb leírás: itt.

Táblák

Áttekintés

A táblák a kdb+ adatbázis alapjai. A tábla egy oszlopokból álló gyűjtemény, amely szótárként van implementálva. Következésképpen a q táblák oszlop-orientáltak, ellentétben a sor-orientált relációs adatbázisokkal. Továbbá az oszlop értékei egy rendezett listát alkotnak, szemben az SQL-lel, ahol a sorok sorrendje nem definiált. Ez a tény - hogy a q táblák rendezett oszloplisták - teszik a kdb+ adatbázist nagyon hatékonnyá mind az adattárolás, a visszakeresés, valamint az adatmanipuláció műveleteit rendezett adatokon. Egy fontos példa erre, mikor fontos az adatok időrendisége.

A kdb+ képes kezelni mind relációs, mind idősoros adatot a q táblákon keresztül. Nincs elkülönült adatdefiniáló nyelv, és nincsenek külön tárolt függvények. Csupán q táblák, kifejezések és függvények.

A táblák a szótárakból épülnek fel, ezért a szótárakról szóló részt mindenképp el kell olvasni előbb.

Definíció

A tábla egy oszlop-szótár elforgatása

A tábla nem más, mint egy oszlop-szótárnak az elforgatása (vagyis transzponálása). Minden tábla a 98h típus-azonosítóval rendelkezik. Például:

d:`name`iq!(`Dent`Beeblebrox`Prefect;98 42 126) d[`iq;] 98 42 126
d[;2] name | `Prefect iq | 126
d[`iq;2] 126
t: flip `name`iq!(`Dent`Beeblebrox`Prefect;98 42 126) t[;`iq] 98 42 126
t[2;] name | `Prefect iq | 126
t[2;`iq] 126
Ahogy a példa mutatja, hogy hozzáférjünk az elemekhez egy t táblában, amelyet úgy kaptunk, hogy egy d oszlop-szótárat elforgattunk, csupán fordítsuk meg az argumentumokat d projekcióiban. Az i és j indexek is szerepei is fel vannak cserélve, hogy még természetesebb legyen a használatuk a táblák szemszögéből:
t[i;] / i-edik sor egy szótár oszlopnév és érték között t[i] / i-edik eleme a t listának ..ugyanaz, mint az előző t[;cj] / oszlopértékek vektora a cj oszlopból
Oszlop és sor érték visszakeresése így megfelel a konvencionális mátrix-szerű jelölésnek, ahol az első index határozza meg a sort, a második pedig az oszlopot.

Táblák megjelenítése

Figyeljük meg, hogy egy tábla oszlopainak és sorainak a megjelenítése megfelel a szótár-féle megjelenítés transzponáltjának, még ha a belső adatszerkezet ugyanaz is.

d name| Dent Beeblebrox Prefect iq | 98 42 126
t name iq -------------- Dent 98 Beeblebrox 42 Prefect 126
Tábladefiníció szintaxisa

Táblát nem csak oszlop-szótár flip-pelésével hozhatunk létre, hanem van erre saját szintaxis is:

([] c1:L1;...;cn:Ln)

Itt c1 egy azonosító, ami egy oszlopnevet tartalmaz, L1 pedig a hozzátartozó lista, ami az oszlop értékeit tartalmazza. A listáknak azonos számosságúaknak kell lenniük. A szögletes zárójel célja a kulcs meghatározása. Lehet oszlopnevek nélkül is definiálni táblát, ekkor az oszlopok rendre az x, x1, x2... neveket kapják. Példa a korábbi t tábla definiálására ilyen formában:

t:([] name:`Dent`Beeblebrox`Prefect; iq:98 42 126) t[;`iq] 98 42 126
t[2;] name | `Prefect iq | 126
t[2;`iq] 126

t ilyetén módon történő definiálása egyértelműen tisztább és érthetőbb, mint egy oszlop-szótár transzponálása.

Az oszlopokat váltózókban is tárolhatjuk, ami hasznos lehet programozási szempontból:

c1:`Dent`Beeblebrox`Prefect c2:98 42 126 t:([]c1;c2) t c1 c2 -------------- Dent 98 Beeblebrox 42 Prefect 126

Megjegyzés: Amikor az összes Li lista singleton, akkor enlist-elni kell őket.

Megjegyzés: Ha legalább egy oszlop lista, és a többi oszlop közül egy vagy több atom, akkor minden atomi oszlop kiterjesztődik listának, aminek a számossága megegyezik a többi oszlopéval:

tdef:([]c1:`a`b`c; c2:42; c3:1.1 2.2 3.3) tdef c1 c2 c3 --------- a 42 1.1 b 42 2.2 c 42 3.3
Metaadatok

Az oszlopnevek kinyerhetőek egy táblából a cols függvényt használva:

cols t `name`iq

A pont (.) operátort használva lehetőség van egy tábla oszlopát visszakapnunk. Például egy t tábla c oszlopát a t.c kifejezéssel is ki tudjuk nyerni:

t.name `Dent`Beeblebrox`Prefect
t.iq 98 42 126

A meta függvénnyel kinyerhetjük egy tábla összes metaadatát. Az eredmény egy olyan tábla, amelynek annyi sora van, ahány oszlopa t-nek volt. A tábla kulcsa a c oszlop, ez tartalmazza a tábla oszlopneveit. A t oszlop tartalmazza típust jelző szimbólumot az adott oszlophoz. Az f oszlop a külső kulcsokra vonatkozik. Az a oszlop tartalmaz bármilyen attribútumot, ami az adott oszlopra vonatkozik:

meta t c | t f a --| ----- c1| s c2| i
Rekordok

A count függvény visszatér egy tábla sorainak a számával:

count t 3

Egy sort egyszerűen visszakaphatunk a szögletes zárójel operátort használva:

t[1] name| Beeblebrox iq | 98

Üres táblák és sémák

Ahogy az előző szekcióban láthattuk, egy táblát lehet definiálni és feltölteni egy külön erre való szintaxissal:

t:([] name:`Dent`Beeblebrox`Prefect; iq:98 42 126)

Ezt a gyakorlatban ritkán használjuk, ugyanis vagy túlságosan sok értéket kell felvenni a táblába, vagy pedig később akarjuk azt feltölteni. Ilyen körülmények között hasznos létrehozni egy üres táblát, majd feltölteni azt elemekkel később. A következő példában az üres zárójelek az üres listát jelölik:

t:([] name:(); iq:())

A táblát később feltölthetjük értékekkel, például egy fájlból kiolvasva azokat.

Amikor egy táblát hozunk létre a fenti módon, az oszlopok általános típusú elemeket tartalmazó üres listák. Így bármilyen adattal fel lehet őket tölteni. Az oszlop típusát aztán az első behelyezett elem fogja meghatározni, az azt követő elemeknek ugyanolyan típusúaknak kell lenniük.

Lehetőség van azonban egy üres tábla létrehozásakor egyből meghatározni az oszlopok típusát. Ilyenkor csupán egy null-listát kell megadni a megfelelő típussal:

t:([] name:`symbol$(); iq:`int$())

Kulcsos táblák és idegen kulcsok

A kulcsos tábla még egy fokkal bonyolultabb adatszerkezet, mint a hagyományos tábla. Egy olyan szótár, amelynek a kulcs- és értéktartománya is tábla. Az egyszerűsített táblaszintaxisban a szögletes zárójelek között megadhatunk kulcsoszlopokat, így egyszerűen készíthetünk kulcsos táblát:

kt:([eid:1001 1002 1003] name:`Dent`Beeblebrox`Prefect; iq:42 98 126) kt eid | name iq ----| -------------- 1001| Dent 42 1002| Beeblebrox 98 1003| Prefect 126

Kulcsos táblából indexeléssel kikereshetjük egy adott kulcshoz tartozó értéket.

kt[1001] name | `Dent iq | 42

Kulcsos táblához létrehozhatunk idegen kulcsot is. Ehhez a tábla nevét kell használnunk típus helyett egy oszlopnál. Az idegen kulcsoknak a q-sql lekérdezésekben van szerepük.

tdetails:([] eid:`kt$1003 1001 1002 1001 1002 1001; sc:126 36 92 39 98 42)

Bővebb információk a táblákról: itt.