# 以下は2020年頃に執筆した過去ブログのアーカイブです。現在メンテしておらず、一部の情報が古い可能性があります
へんてこデータベースもどき「kdb」と、知る人ぞ知るその実装言語「q」の基本を解説する本シリーズ。
本連載の目的はq言語の基本的な情報、実務で用いられる実践的なテクニックを日本語で分かりやすく提供することである。細かい情報や定義を知りたい時は適宜「q for mortals」及び「q Tips」を参照されたい
q for mortals
https://code.kx.com/q4m3/2_Basic_Data_Types_Atoms/
q Tips
https://www.amazon.com/Tips-Fast-Scalable-Maintainable-Kdb-ebook/dp/B00UZ8OMME
Dictionary
Dictionaryは一対一に紐づくkey, valueのマップで、それぞれがListである。各keyの値はユニークである必要があり、それぞのListはsimple listとgeneral listどちらの場合もある。*1
DictionaryはDictionaryであると同時に、2つのListのペアとして捉えられる点がポイントである。
Dictionaryの宣言
keyとなるListとvalueとなるlistの間に!(bang)を置くことで宣言される。
keys!values
q)d:1 2 3!`hoge`moge`fuga q)d 1| hoge 2| moge 3| fuga
valueは以下のようにして取出し可能。
q)d[1] `hoge q)d[2] `moge
存在しないkeyを与えた場合単にnullが返る。
q)d[1111] `
?(Find)オペレータを用いることで、value→keyの逆引きも可能。
q)d?`hoge 1
key、valueのListはそれぞれ以下の関数で取出せる。
q)key d 1 2 3 q)value d `hoge`moge`fuga q)count d 3
※一般的な言語における辞書型との相違点として、qにおいては中身のカバレッジが同じであっても、内部のListの順序が異なるDictionaryは等価と見做されない。
q)d1:1 2 3!`hoge`moge`fuga q)d2:1 3 2!`hoge`fuga`moge q)d1~d2 0b
Dictionaryの操作
存在するkeyにvalueをアサインした場合はamend、存在しないkeyにvalueをアサインした場合は追加となる。
q)d:1 2 3!`hoge`moge`fuga q)d[1]:`puga q)d 1| puga 2| moge 3| fuga q)d[4]:`poga q)d 1| puga 2| moge 3| fuga 4| poga
cutを使用することで要素を削除できる。オペランド”_”でも同じ効果が得られ、両者は恐らく等価である。
なんでこんな紛らわしい予約語作ったんだ…
q)d 1| hoge 2| moge 3| fuga q)1 2 cut d 3| fuga q)(enlist 1) cut d / 削除する対象のkeyはListで渡さないとダメ 2| moge 3| fuga q)1 2 _ d /アンダースコアでも同じ効果が得られるのだが、使いどころは謎だ… 3| fuga q)(enlist 1) _ d 2| moge 3| fuga
Dictionaryの応用
Dictionaryを関数の引数として与えることで、各valueに関数を適用することが出来る。
q)d2:`hoge`fuga`hoga!1 2 3 q)d2 hoge| 1 fuga| 2 hoga| 3 q)neg d2 hoge| -1 fuga| -2 hoga| -3 q)d2=20 hoge| 0 fuga| 0 hoga| 0 q)d2*20 hoge| 20 fuga| 40 hoga| 60
,(join)同士のjoinも可能で、keyが重複した場合右側のDisctionaryが優越する。
q)d1:`hoge`fuga`hoga!1 2 3 q)d2:`hoge`pega!10 20 q)d1,d2 hoge| 10 fuga| 2 hoga| 3 pega| 20
,(join)の亜種として^(Coalesce)というのもある。
keyが重複した場合右側が優先される点も含めて挙動はjoinとほぼ同じだが、こちらは右側のListのvalueがNULLであった場合のみ左側の値で上書きされる。
q)d1:`hoge`fuga`hoga!1 2 3 q)d2:`hoge`fuga`hoga!10 0N 30 q)d1^d2 hoge| 10 fuga| 2 hoga| 30
Tableの誕生
本連載を読み進める中で、一部の読者は幾らかのフラストレーションを感じてきたのではないかと想像している。
オレはデータベース勉強したくてqに入門したのに、何時まで経ってもTable出てこねーじゃねぇか!、と。
その辛抱も今日で終わりだ。ListとDictionaryを修めた今、読者諸兄はq/kdbにおけるTableを召喚する準備が出来ている。
先ずは完成形をお見せしよう。
q)flip `country`area`population!(`Japan`Germany`Kazakhstan;`Asia`Europe,`$"Central Asia";126.5 83.02 18.28) country area population ---------------------------------- Japan Asia 126.5 Germany Europe 83.02 Kazakhstan Central Asia 18.28
どうだろう、データベースのテーブルとして馴染み深いフォーマットに見えるのではないだろうか?
宣言部は一見面食らうかもしれないが、よく見ればflip以外は目新しい要素はないはずだ。
先ずはflipなしで見てみよう。これは`country`area`populationをkeyとするDictionaryで、それぞれがvalueとして配列を保持している。
key'country'の値はJapan Germany Kazakhstan、key'area'の値はAsia Europe Central Asia....という具合だ。
q)`country`area`population!(`Japan`Germany`Kazakhstan;`Asia`Europe,`$"Central Asia";126.5 83.02 18.28) country | Japan Germany Kazakhstan area | Asia Europe Central Asia population| 126.5 83.02 18.28
flipは、与えられたデータのx軸とy軸を反転する関数である。
q)((1 2 3 4);(5 6 7 8)) 1 2 3 4 5 6 7 8 q)flip ((1 2 3 4);(5 6 7 8)) 1 5 2 6 3 7 4 8
これを先ほどのDictionaryに適用するとどうなるだろう?
q)flip `country`area`population!(`Japan`Germany`Kazakhstan;`Asia`Europe,`$"Central Asia";126.5 83.02 18.28) country area population ---------------------------------- Japan Asia 126.5 Germany Europe 83.02 Kazakhstan Central Asia 18.28
Voilà!*2
これこそがkdbにおけるテーブルなのだ。type関数の返り値もテーブル型を意味する98を示す。
q)Country:flip `country`area`population!(`Japan`Germany`Kazakhstan;`Asia`Europe,`$"Central Asia";126.5 83.02 18.28) q)type Country 98h
テーブル型のデータはq-sql*3によってクエリを掛けることが出来る。
q)select from Country country area population ---------------------------------- Japan Asia 126.5 Germany Europe 83.02 Kazakhstan Central Asia 18.28 q)select from Country where country=`Japan country area population ----------------------- Japan Asia 126.5 q)select from Country where population > 100 country area population ----------------------- Japan Asia 126.5 q)select from Country where population < 100 country area population ---------------------------------- Germany Europe 83.02 Kazakhstan Central Asia 18.28
どうだろう、一気にデータベーステーブルを扱っている感が出てきたのではないだろうか?
ここまで読んでいただいた皆様にはお分かりの通り、kdbにおけるテーブルというのは、Listを要素として持つDictionaryをflipしたものに過ぎず、他のデータベースで扱われるような、カプセル化された特別なデータ型というわけではない。
従って、テーブルデータはqの実装を通じて、自由自在に変形、加工することが出来る。
これこそが筆者がkdbを”データベースみたいなもの”と呼ぶ所以であり、kdbというプロダクトを特徴づける特色なのである。
また、テーブルの各カラムは元々がDictionaryのKey/valueであることから、kdbは本質的に列指向データベースであると言える。
詳しいTableの挙動については、Tableの章(次の次ぐらいかな?)で詳述する。