kdb和jupyter的配置

本文主要介绍: 配置kdb和Python交互的jupyter-notebook环境:使用pyqpyq的安装与使用1. pyq的jupyter环境配置由于本文使用的是32bit免费版本的kdb+(从官网下载windows.zip),所以Python也需要是32位版本的。 环境要求: 安装必须使用windows的cmd环境Python须为3.6版本(如不是,需要创建虚拟环境,下面会介绍)1.1 Python3.6虚拟环境的配置 如果默认已经是python3.6的32位版本,则本节可以跳过。 否则,在windows下,使用conda命令创建32位的虚拟环境: set CONDA_FORCE_32BIT=1 conda create -n py36_32 python=3.6 这样会在C:\Anaconda3\envs下生成一个py36_32的虚拟环境,使用如下命令可以激活该环境: activate py36_32可以看到在激活之后,python变为32位的3.6版本:pip版本: 接下来的步骤都要在此虚拟环境下进行。 1.2 Jupyter环境的配置 接下来,严格按照Install Jupyter Kernel上的步骤: 这里前两步已经不用再做了;%VIRTUAL_ENV%就是1.1中创建的python虚拟环境的位置,即C:\Anaconda3\envs\py36_32;第4步,将从官网下载windows.zip解压到%VIRTUAL_ENV%目录下;剩下的按步骤进行。在这里配置环境变量QHOME和QBIN: QHOME: %VIRTUAL_ENV%\qQBIN: %QHOME%\w32\q.exe 以及Path中添加: C:\Anaconda3\envs\py36_32\q\w32C:\Anaconda3\envs\py36_32\q全部完成之后,执行(仍需在虚拟环境下): pip install jupyter pyq pyq-kernel再执行 %QBIN% python.q -mpyq.kernel install即安装完毕。完成之后,执行下面代码可以检验是否安装成功: >jupyter kernelspec listAvailable kernels: pyq_3 C:\Users\a\AppData\Roaming\jupyter\kernels\pyq_3 python3 c:\users\a\py36\share\jupyter\kernels\python32. PYQ的配置依然是在python3.6 32位的虚拟环境下,执行: pip install -U pyq在虚拟环境(python3.6 32bit)下启动pyq,否则会报错找不到python.dll: q python.q 之后便可以使用pyq的功能,具体使用不再介绍,移步pyq user guide。 3. Jupyter 展示先激活虚拟环境: activate py36_32启动jupyter: jupyter notebook在new下选取PyQ3即可: ...

May 28, 2019 · 1 min · jiezi

8. Q语言学习之路—表

概述表(Tables)和列表、字典一样,都是q语言中的第一类对象(First-class entity)Q表是列导向的Q表是从字典构建的1.表的定义1. 表作为列字典q)dc:nameiq!(DentBeeblebroxPrefect;98 42 126)q)dc[iq;]98 42 126q)dc[;2]name| Prefectiq | 126q)dc[iq; 2]126我们通过flip指令来得到一个表q)t:flip nameiq!(DentBeeblebroxPrefect;98 42 126)q)t[;iq]98 42 126q)t[2;]name| Prefectiq | 126q)t[2;iq]126所有表的类型都是98h。类似于python的pandas包,可以通过t[column]或者t.column来获取一个列。但请注意,点操作符在函数内不起作用。q)t[nameiq]Dent Beeblebrox Prefect98 42 1262. 表的显示q)dcname| Dent Beeblebrox Prefectiq | 98 42 126q)tname iq --------------Dent 98 Beeblebrox 42 Prefect 1263. 表定义语法定义语法为([] c1:L1;...;cn:Ln)其中ci是列名,Li是对应的列值。其中的冒号不是必须的。q)t:([] name:DentBeeblebroxPrefect; iq:98 42 126)q)tflip nameiq!(DentBeeblebroxPrefect;98 42 126)1b表定义中也可以直接使用变量,这样对应的列名就是变量名了q)c1:DentBeeblebroxPrefectq)c2:98 42 126q)([] c1; c2)c1 c2————–Dent 98Beeblebrox 42Prefect 126atom元素会自动拓展到列长度q)([] c1:abc; c2:42; c3:98.6)c1 c2 c3 ----------a 42 98.6b 42 98.6c 42 98.64. 表的元数据可以通过cols指令来获取表的列;可以通过meta来获取表的元数据,结果是一个键表(keyed-table)。q)meta tc | t f a----| -----name| s iq | j其中t代表了列的类型,f表示了是否存在任何外键。指令tables返回当前命名空间下的表名, \a效果相同:q)t2:([] c1:1 2 3; c2:(1 2; enlist 3; 4 5 6))q)t:([] name:DentBeeblebroxPrefect; iq:98 42 126)q)tables .tt25. 记录表在逻辑上是一个字典记录(record)的列表,count返回记录的个数。q)count t3为了获取表中的不带列名的行,可以使用value函数q)value t[1]Beeblebrox426. 转置的列字典 vs 记录的列表逻辑上,表既可以是转置的列字典(Flipped column dictionary),也可以是记录的列表(list of records),但是物理上它被储存为列字典。事实上,Q语言会自动地识别满足表形式的列字典,在没有任何请求询问下,将其转换为表形式。例如下两例:q)type (nameiq!(Dent;98); nomeiq!(Beeblebrox;42))0hq)type (nameiq!(Dent;98); nameiq!(Beeblebrox;42))98h表被储存为列形式的一个坏处是,当想要删除掉表中的一行时,花销非常大。The best way to deal with this in large tables is not to do it.与其删除一行,不如用一个表示flag的column来表示该行有没有被删除。2. 空表和模式创建一般空表的方式:q)([] name:(); iq:())推荐的做法是给每列指定类型q)([] name:symbol$(); iq:int$())一种等价的创建带类型的表的方法:q)([] name:0#; iq:0#0)q)([] name:0#; iq:0#0) ([] name:symbol$(); iq:int$())1b3. 基本的select和update操作下面以这个表的操作为例q)t:([] name:DentBeeblebroxPrefect; iq:98 42 126)1. select的语法 基本语法为:select cols from table 取全部列直接省略掉cols即可,不用加*。2. 结果显示select的结果永远是一个表。q)select from tname iq --------------Dent 98 Beeblebrox 42 Prefect 1263. 选择列q)select name from tname----------DentBeeblebroxPrefectq)select c1:name, c2:iq from t4. 基本的updateupdate的语法与select的语法基本相同,其使用冒号右边的值来取代该列。q)update iq:iq%100 from tname iq---------------Dent 0.98Beeblebrox 0.42Prefect 1.264. 主键和键表在SQL中,可以声明一个或多个列作为主键,主键的值是unique的,使得通过主键的值来提取行成为可能。1. 键表定义:A keyed table is a dictionary mapping a table of key records to a table of value records. 一个键表并不是一个表——它是一个字典,所以类型是99h。2. 简单的例子值的表(table of value):q)v:flip nameiq!(DentBeeblebroxPrefect;98 42 126)键的表(table of key):q)k:flip (enlist eid)!enlist 1001 1002 1003创建键表:q)kt:k!vq)kteid | name iq ----| --------------1001| Dent 98 1002| Beeblebrox 42 1003| Prefect 1263. 键表的定义语法下面是通过字典形式对键表定义的常规语法:q)kt:(flip (enlist eid)!enlist 1001 1002 1003)! flip nameiq!(DentBeeblebroxPrefect;98 42 126)q)kteid | name iq----| --------------1001| Dent 981002| Beeblebrox 421003| Prefect 126直接使用表定义语法将更为简单:q)kt:([eid:1001 1002 1003] name:DentBeebleBroxPrefect; iq:98 42 126)带类型的空键表的定义形式如下:q)ktempty:([eid:int$()] symbol$name:(); iq:int$())q)ktemptyeid| name iq---| -------q)ktempty:([eid:0#0] name:0#;iq:0#0)4. 获取键表的记录因为键表本质上是一个字典映射,所以可以通过键值对记录进行查找。注意键和值都对应的是字典。q)kt[(enlist eid)!enlist 1002]name| Beeblebroxiq | 42上例可以简化为:q)kt[1002]name| Beeblebroxiq | 42我们可以获取某个列的值q)kt[1002][iq]42q)kt[1002;iq]425. 取多行的记录 q)kt[1001]name| Dentiq | 98q)kt 1001name| Dentiq | 98为了取多行的数据,通过如下的方式去取是错误的q)kt[1001 1002]'length正确的方式是:q)kt[(enlist 1001;enlist 1002)]name iq-------------Dent 98Beeblebrox 42q)kt[flip enlist 1001 1002]也可以通过构建一个匿名的表来取值q)kt ([] eid:1001 1002)name iq-------------Dent 98Beeblebrox 42回想第5.2.2节指出的,可以通过命令#来提取一个子字典,对于键表同理:q)([] eid:1001 1002)#kteid | name iq----| -------------1001| Dent 981002| Beeblebrox 426. 反向查找由于键表是一个字典,所以我们通过值来反响查找键。q)kts:([eid:1001 1002 1003] name:DentBeeblebroxPrefect)q)ktseid | name—-| ———-1001| Dent1002| Beeblebrox1003| Prefect反向查找:q)kts?([] name:PrefectDent)eid—-100310017. 键表的成分可以通过key和value来提取键表成分q)key kteid—-100110021003q)value ktname iq————–Dent 98Beeblebrox 42Prefect 126函数keys和cols返回键表的键名和列名。8. 表和键表通过xkey来设置一列为键q)t:([] eid:1001 1002 1003; name:DentBeeblebroxPrefect; iq:98 42 126)q)eid xkey teid | name iq —-| ————–1001| Dent 98 1002| Beeblebrox 42 1003| Prefect 126在xkey左运算元设置为空列表时转换为常规表q)kt:([eid:1001 1002 1003] name:DentBeeblebroxPrefect; iq:98 42 126)q)() xkey kteid name iq -------------------1001 Dent 98 1002 Beeblebrox 42 1003 Prefect 126也可以使用!的又一重载用法:左运算元为一个非负的数字,代表左侧将包含在键中的列数;右运算元为表或者键表。0代表没有键。q)1!tq)0!kteid name iq -------------------1001 Dent 98 1002 Beeblebrox 42 1003 Prefect 126q)2!0!kteid name | iq---------------| ---1001 Dent | 981002 Beeblebrox| 421003 Prefect | 126上述返回的结果都是在表的拷贝上进行的,如果要对表进行in-place的操作,则需要使用call-by-nameq)eid xkey ttq)() xkey ktktq)kteid name iq ——————-1001 Dent 98 1002 Beeblebrox 42 1003 Prefect 1269. 复合的主键 q)ktc:([lname:DentBeeblebroxPrefect; fname:ArthurZaphodFord]; iq:98 42 126)q)ktclname fname | iq —————–| —Dent Arthur| 98 Beeblebrox Zaphod| 42 Prefect Ford | 126复合主键的查找可以通过一个复合的键:q)ktc[lnamefname!BeeblebroxZaphod]iq| 42对于一个简单的键,我们可以简化查找:q)ktc[DentArthur]iq| 98空的多键值表的创建:q)ktc:([lname:symbol$();fname:symbol$()] iq:int$())q)ktc:([lname:0#;fname:0#] iq:0#0)10. 提取复合键可以通过复合键的列表来提取对应的记录q)ktc (DentArthur;PrefectFord)iq---98126当然也可以通过匿名表来提取q)ktc ([] lname:DentPrefect; fname:ArthurFord)当然也可以用#来提取子字典q)K:([] lname:DentPrefect; fname:ArthurFord)q)K#ktclname fname | iq --------------| ---Dent Arthur| 98 Prefect Ford | 12611. 提取列数据在这一小节中我们使用如下两个例子:q)kts:([k:101 102 103] v1:abc; v2:1.1 2.2 3.3)q)kts_q)ktc:([k1:abc;k2:xyz] v1:abc; v2:1.1 2.2 3.3)q)ktc_提取特定列的做法:q)kts[([] k:101 103)][v1]acq)ktc[([] k1:ac;k2:xz)][v1v2]也可以使用index at depth的做法q)kts[([] k:101 103); v1]_q)ktc[([] k1:ac;k2:xz); v1v2]_5. 外键和虚拟列外键是指表中的一列,该列的数据都在另一个表中的主键中。1. 外键的定义定义:A foreign key is one or more table columns whose values are defined as an enumeration over the key column(s) of a keyed table. 2, 外键的例子以下表为例q)kt:([eid:1001 1002 1003] name:DentBeeblebroxPrefect; iq:98 42 126)以eid列为外键的表定义如下,这里用到了枚举,强制保证了该列出现的值都在另一个表的键值中q)tdetails:([] eid:kt$1003 1001 1002 1001 1002 1001; sc:126 36 92 39 98 42)在meta命令中,可以看到外键出现在f列中:q)meta tdetailsc | t f a---| ------eid| j kt sc | j 内置函数fkeys返回一个表示外键的字典q)fkeys tdetailseid| kt3. 清除外键当想清楚一个外键时,对枚举的列使用value函数q)meta update value eid from tdetailsc | t f a---| -----eid| j sc | j 4. 外键和关系设tf是一个带有键表kt外键f的表,为了获取kt中的某一列c,可以直接在select语句中直接使用点操作符f.c即可。q)select eid.name, sc from tdetailsname sc --------------Prefect 126Dent 36 Beeblebrox 92 Dent 39 Beeblebrox 98 Dent 42 6. 使用表和键表在这一节中,我们使用如下例子q)t:([] name:DentBeeblebroxPrefect; iq:98 42 126)q)kt:([eid:1001 1002 1003] name:DentBeeblebroxPrefect; iq:98 42 126)1. Append记录使用,:,带列名时可以不同考虑顺序q)t,:nameiq!(W; 26)q)t,:iqname!(200; Albert)q)tname iq--------------Dent 98Beeblebrox 42Prefect 126Albert 200不带列名时必须考虑顺序q)t,:(H; 142)_q)t,:(97;J)'typeq)t_2. First和Last记录使用函数first和lastq)first tname| Dentiq | 98q)last tname| Albertiq | 200使用#获取前后几行记录q)2#tq)-3#kt3. Findfind操作符?返回记录的索引q)t?nameiq!(Dent;98)0上式可简化为q)t?(Dent;98)0也可以查多列q)t?((Dent;98);(Prefect;126))0 2由于键表是一个字典,所以使用?相当于执行了反向查找,返回对应的键值q)kt?nameiq!(Dent;98)eid| 1001q)kt?(Dent;98)eid| 1001单个列的查找必须生成列值,或者使用匿名表的结构q)t1:([] eid:1001 1002 1003)q)t1?enlist each 1001 10020 1q)t1?([] eid:1001 1002)0 14. Union with,使用join操作符,追加记录到一个表中,但是不会执行类型检查。q)t,nameiq!(Slaartibartfast; 123)name iq --------------------Dent 98 Beeblebrox 42 Prefect 126 Slaartibartfast 123只有表有完全一样的meta结果时,两个表才能合并成一个表。q)t,([] name:1#W; iq:1#26)q)t,t当键表使用,合并两个meta结果相同的键表时,右边的键表将upsert(update and insert)左边的键表,不同的键将append,相同的键将update。q)kt,([eid:1003 1004] name:PrefectW; iq:150 26)eid | name iq ----| --------------1001| Dent 98 1002| Beeblebrox 42 1003| Prefect 1501004| W 26 5. Coalesce ^Coalesce^被用来合并两个具有相同列的键表。^的效果与,的相同,除了这种情况:两个键表相同键合并时,右边的键表若为null,则左边的保留下来。q)([k:abc] v:10 0N 30)^([k:abc] v:100 200 0N)k| v-| ---a| 100b| 200c| 30q)([k:abcx] v:10 0N 30 40)^([k:abcy]; v:100 200 0N 0N)k| v-| ---a| 100b| 200c| 30x| 40y|^的执行效率没有,的高,因为右运算元需要检查是否为null。6. 列 Join两个具有相同数量记录的表可以使用join-each(,')来合并列。q)([] c1:abc),’([] c2:100 200 300)c1 c2 ——a 100b 200c 300在键表上的列join:q)([k:1 2 3] v1:10 20 30),’([k:3 4 5] v2:1000 2000 3000)k| v1 v2 -| ——-1| 10 2| 20 3| 30 10004| 20005| 30007. 复杂列数据1. 简单的例子在如下表中,lh列中储存了一个嵌套的列表q)tp:([] d:2015.01.01 2015.01.02; lh:(67.9 82.10; 72.8 88.4))q)tp 0d | 2015.01.01lh| 67.9 82.1q)tp lh67.9 82.172.8 88.42. 复合列数据的操作复合列数据的定义:嵌套列中的元素都是简单列表。q)tm:([] wk:2015.01.01 2015.01.08; rv:(38.92 67.34; 16.99 5.14 128.23 31.69))q)tmwk rv ----------------------------------2015.01.01 38.92 67.34 2015.01.08 16.99 5.14 128.23 31.69Nested columns mean adverbs. Lots of adverbs.q)select wk, srt:desc each rv, avgr:avg each rv, hi:max each rv from tmwk srt avgr hi -------------------------------------------------2015.01.01 67.34 38.92 53.13 67.34 2015.01.08 128.23 31.69 16.99 5.14 45.5125 128.23q)select wk, drp:neg 1_'deltas each desc each rv from tmwk drp ---------------------------2015.01.01 ,28.42 2015.01.08 96.54 14.7 11.853. 复合外键复合主键的键表:q)ktc:([lname: DentBeeblebroxPrefect; fname:ArthurZaphodFord]; iq:98 42 126)有ktc外键的表:q)tdetails:([] name:ktc$(BeeblebroxZaphod;PrefectFord;BeeblebroxZaphod); sc:36 126 42)ktc的列就可以作为tdetails的虚拟列了q)select name.lname, name.iq, sc from tdetailslname iq sc ——————Beeblebrox 42 36 Prefect 126 126Beeblebrox 42 42 8. 属性Attributes are metadata that you attach to lists of special forms. They are also used on a dictionary domain or a table column to speed retrieval for some operations.属性是描述性的,当你指定表中某一列具有某种属性时,q语言会去检查确认,但并不会帮你完成这个操作。Kx says not to expect significant benefit from an attribute for fewer than a million items.1. Sorted s# 当某一列被指定为sorted的时候,线性搜索就会被替换为二分查找,查找速度更快。当属性被成功应用于列表时,它就变成了列表的一部分,并且q会检查这个列表是否满足这一属性。q)s#1 2 4 8s#1 2 4 8q)s#2 1 3 4’s-fail排序函数asc会自动在其结果上应用sorted属性,但是til不会q)asc 2 1 8 4s#1 2 4 8q)til 50 1 2 3 4q)L:s#1 2 3 4 5q)L,:6q)Ls#1 2 3 4 5 6q)L,:0q)L1 2 3 4 5 6 0对字典应用sorted属性,会在应用到其键值上,其查找算法就会被替代为二分查找。q)d:s#10 20 30 40 50!abcdeq)key ds#10 20 30 40 50q)d 10aq)d 12aq)d 15aq)d 20b2. Unique u# q)u#2 1 4 8u#2 1 4 8q)u#2 1 4 8 2’u-failUnique属性的amend操作:q)L:u#2 1 4 8q)L,:3q)Lu#2 1 4 8 3q)L,:2q)L2 1 4 8 3 2unique属性可以作用于字典的域,表的一列或者键表的键。但是不能作用于一个字典,表或者键表。3. Parted p# Parted属性 p#表明对所有出现的值,相同的值都是互相邻近的。q)p#2 2 2 1 1 4 4 4 4 3 3p#2 2 2 1 1 4 4 4 4 3 3q)p#2 2 2 1 1 4 4 4 4 3 3 2'u-fail [0] p#2 2 2 1 1 4 4 4 4 3 3 2parted属性在任何list的操作上都不会被保留下来,即使折耳根操作保留了这个属性。q)L:p#1 1 2 3 3q)Lp#1 1 2 3 3q)L,:3q)L1 1 2 3 3 34. Grouped g# 可以作用在任何list上。It causes q to create and maintain an index – essentially a hash table.q)g#1 2 3 2 3 4 3 4 5 2 3 4 5 4 3 5 6q)L:g#100?100q)L,:1 1 1 15. 去除属性 # 通过指令 # 可以去除列表上的任何属性:q)L:s#til 10q)Ls#0 1 2 3 4 5 6 7 8 9q)#L0 1 2 3 4 5 6 7 8 9Section End.

April 11, 2019 · 5 min · jiezi

7. Q语言学习之路—数据转换

类型1. 基础数据类型2. type操作符type操作符返回一个short,并且如果对象是一个atom,则返回负数;是一个list则返回正数。q)type 42-7hq)type 10 20 307hq)type 98.6-9hq)type 1.1 2.2 3.39hq)type a-11hq)type abc11hq)type “z”-10hq)type “abc"10h任何常规列表(general list)的类型都是0h。q)type (42h; 42i; 42j)0hq)type (1 2 3; 10 20 30)0hq)type ()0h任何字典(包括键表)的类型,都是99h。q)type (abc!10 20 30)99hq)type ([k:abc] v:10 20 30)99h任何表的类型都是98h。q)type ([] c1:abc; c2:10 20 30)98h3. 变量的类型q语言是动态类型语言,所以变量的类型也会随着其被赋的值而改变。全局变量会被储存在一个一般的q字典中。可以通过命令get . 来查看全局变量和对应的值,也可以通过操作符value来查看q)value .q)a:42q)value .a| 42q)f:{xx}q)value .a| 42f| {x*x}..2. Cast转换使用dyadic操作符$,右边的运算元是原始值,而左边的运算元是目标类型。有三种方式指定目标类型:数值的short类型char类型值类型的symbol名称1. Casts that Widen因为目标类型比原始类型要宽,所以在这种情况下没有信息丢失。q)7h$42i / int to long42q)6h$42 / long to int42iq)9h$42 / long to float42fq)"j"$42i42q)"i"$4242iq)"f"$4242fmore readable:q)int$4242iq)long$42i42q)float$4242f2. 不同类型之间的转换 char类型的underlying value是ASCII码,所以我们可以将char与integer互相转换。q)char$42"*"q)long$"\n"10同样,也可以将日期与integer互相转换q)date$02000.01.01q)int$2001.01.01 / millennium occurred on leap year366i3. Casts that Narrowq)long$12.34512q)short$12345678932767h将数值类型转换为boolean类型,任何0值都是0b,其余的值都是1b。q)boolean$00bq)boolean$0.00bq)boolean$1231bq)boolean$-12.3451b也可以从复杂类型中提取成分q)date$2015.01.02D10:20:30.1234567892015.01.02q)year$2015.01.022015iq)month$2015.01.022015.01mq)mm$2015.01.021iq)dd$2015.01.022iq)hh$10:20:30.12345678910iq)minute$10:20:30.12345678910:20q)uu$10:20:30.12345678920iq)second$10:20:30.12345678910:20:30q)ss$10:20:30.12345678930i4. Casting Integral Infq)int$0Wh32767iq)int$-0Wh-32767iq)long$0Wi2147483647q)long$-0Wi-21474836475. 强制类型回想在向一个简单列表中赋值的时候需要严格match列表的类型q)L:10 20 30 40q)L[1]:42h’typeq)L,:43h’type这种情况可以通过强制类型转换来实现:q)L[1]:(type L)$42hq)L,:(type L)$43h6. Cast is Atomiccast对其右运算元是atomic的:q)“i”$10 20 3010 20 30iq)float$(42j; 42i; 42j)42 42 42fcast对其左运算元也是atomic的:q)shortintlong$4242h42i42q)“ijf”$98.699i9998.6Cast对左右运算元同步的atomic:q)“ijf”$10 20 3010i2030f3. 数据和文本的转换1. 数据转换为string使用函数string可以将任何q元素转换为一个合适的文本表示。string的一些特征:转换结果是一个char的列表,不会是单个的char。转换结果不会包含任何q类型提示符或者其它装饰符。string作用在一个真正的string(list of char)上可能不会得到你想要的结果q)string 42"42"q)string 4,“4"q)string 42i"42"q)a:2.0q)string a,“2"q)f:{xx}q)string f”{xx}“string是伪atomic的,它会作用在运算元的每个atom数据上,但对每个atom元素的运算结果都返回一个listq)string 1 2 3,“1”,“2”,“3"q)string “string”,“s”,“t”,“r”,“i”,“n”,“g"q)string (1 2 3; 10 20 30),“1” ,“2” ,“3"“10” “20” “30"2. 从String构建Symbol从string去创建symbol是一个foolproof的做法。 但是也是唯一的去创建带有空格或者其它特殊符号symbol的方法。其转换使用 $ 。q)$“abc”abcqq)$“Hello World”Hello World注意带有转义符的转换:q)$“Zaphod "Z""Zaphod "Z"q)$“Zaphod \n”ZaphodNote: 左右去空格。q)string $” abc ““abc"3. 从String中解析数据使用大写的目标类型作为左运算元,string作为右运算元。q)“J”$“42"42q)“F”$“42"42fq)“F”$“42.0"42fq)“I”$“42.0"0Niq)“I”$” “0Ni日期转换:q)“D”$“12.31.2014"2014.12.31q)“D”$“12-31-2014"2014.12.31q)“D”$“12/31/2014"2014.12.31q)“D”$“12/1/2014"2014.12.31q)“D”$“2014/12/31"2014.12.31可以从字符串中解析一个函数,使用内置的函数value或者parseq)value “{xx}"{xx}q)parse “{xx}"{x*x}4. 创建带类型的空list空的list的类型是0h,但是当append一个元素之后,得到的sigleton列表就是该元素类型的简单列表。q)L:()q)type L0hq)L,:42q)type L7h如果想规定空list的类型,可以使用类似如下语法q)c1:float$()q)c1,:42'typeq)c1:98.6同样,下面的操作也能产生带类型的空listq)0#0long$()q)0#0.0float$()q)0#``symbol$()5. Enumerations1. 传统的枚举In traditional languages, an enumerated type is a way of associating a series of names with a corresponding set of integral values.2. 数据标准化q)u:gaaplmsftibmq)v:1000000?uq)k:u?vq)k2 1 1 3 3 1 0 0 0 3 0 2 2 1 2 3 1 0 1 1 2 1 2 0 2 1 1 0 1 1 3 0..上述关系满足映射:v=u*k,使用u和索引k来储存v可以大大节省储存空间,并且提高查找效率。3. Enumerating Symbols将一个symbols列表转换为对应的索引列表的操作称为q中的枚举。它以$(又一重载)作为运算符,使用unique的symbols变量作为左运算元,在变量域的symbol的列表作为右运算元。q)u$vu$msftaaplaaplibmibmaaplgggibmgmsftmsftaaplmsft..可以通过强制转换为int来恢复上述的索引结果q)ev:u$vq)int$ev2 1 1 3 3 1 0 0 0 3 0 2 2 1 2 3 1 0 1 1 2 1 2 0 2 1 1 0 1 1 3 ..枚举symbol的基本形式为: u$v 其中u是unique symbol的简单列表,v是在u中出现的atom或者对应的列表。我们称u为枚举的域(domain),投影 u$ 为在u上的枚举。 应用枚举 u$ 在向量v上可以得到索引列表k。4. 使用枚举的symbolq)sym:gaaplmsftibmq)v:1000000?symq)ev:sym$v枚举的ev可以在几乎所有的场景中代替v。q)v[3]aaplq)ev[3]u$aaplq)v[3]:ibmq)ev[3]:ibmq)v=ibm000100010010011101000010010100000000100100000001000000001100001001011..q)ev=ibm000100010010011101000010010100000000100100000001000000001100001001011..q)where v=aapl4 5 19 20 21 31 33 34 41 42 43 49 58 59 61 74 81 83 90 94 95 98 114..q)where ev=aapl4 5 19 20 21 31 33 34 41 42 43 49 58 59 61 74 81 83 90 94 95 98 114..q)v?aapl4q)ev?aapl4q)v in ibmaapl000111010010011101011110010100010110100101110001010000001111011001011..q)ev in ibmaapl000111010010011101011110010100010110100101110001010000001111011001011..尽管枚举和原始列表是item-wise的相等,但是他们并不identical(不match)q)all v=ev1bq)v~ev0b5. 枚举的类型每一个枚举都会被赋予一个新的数值类的数据类型,从20h开始。20h是留给系统的枚举域的,你自己定义的枚举类型则从21h开始,并且逐个增加。类型的正号为simple list,负号为atom的性质依旧保持。q)sym1:gaaplmsftibmq)type sym1$1000000?sym121hq)sym2:abcq)type sym2$c-22h从不同域创建的枚举是不同的,即使他们的组成都相同q)sym1:cbaq)sym2:cbaq)ev1:sym1$abacaq)ev2:sym2$abacaq)ev1=ev211111bq)ev1~ev20b6. 更新一个枚举的列表一个对u简单的改变,可以改变枚举v中对应的所有内容q)sym:gaaplmsftibmq)ev:sym$ggmsftibmaaplaaplmsftibmmsftgibmg..q)sym[0]:twitq)symtwitaaplmsftibmq)evsym$twittwitmsftibmaaplaaplmsftibmmsfttwitibmtwit..相反,则需要对v的每一处进行更新q)vggmsftibmaaplaaplmsftibmmsftgibmg…q)@[v; where v=g; :; twit]7. 动态向枚举域添加直接向枚举append一个域中没出现过的元素时会报错q)sym:gaaplmsftibmq)v:1000000?symq)ev:sym$vq)v,:twtrq)ev,:twtr'cast正确做法是这个新值必须要先添加进域中q)sym,:twtrq)ev,:twtr如果提前不知道域中的全部值,可以使用?(又一重载)来创建一个动态的域。?的语法与枚举重载符$的语法一样。unique的symbol list的名字作为左运算元,源symbol列表作为右运算元。q)sym:()q)sym$g'castq)sym?gsym$gq)sym,gq)sym?ibmaaplsym$ibmaaplq)symgibmaaplq)sym?gmsftsym$gmsftq)symgibmaaplmsft8. 恢复一个枚举可以通过value来恢复一个枚举对应的源symbol列表q)sym:gaaplmsftibmq)v:1000000?symq)ev:sym$vq)value evaaplgmsftmsftibmmsftmsftmsftmsftmsftgibmibmibm..q)v~value ev1b

April 8, 2019 · 2 min · jiezi

6. Q语言学习之路—函数

接下来几章将进入Q语言的核心内容——函数、表、数据转换、查询和I/O等等, excited :)1. 函数说明在q中,函数是可以修改全局变量的,所以q并不是一个纯正的函数式语言。1. 函数定义使用花括号{和},函数的输入输出类型不用指定,甚至函数名都可以不用指定。如下是一个完整的函数定义:q) {[x] xx}调用函数时,参数用中括号包围起来,参数通过分号;分隔q){[x] xx}[3]9函数可以赋值给一个变量:q)f:{[x] xx}q)f[3]92. 函数标识和术语 函数的正式定义为{[p1;…;pn] e1; …; em},其中可选的p1;…;pn是正式的参数,e1; …; em是执行的表达式,虽然是从左向右写的,但是仍然是从右向左执行的。函数的参数个数被称为函数的valence, 最常见的函数是monadic(valence 1)和dyadic(valence 2)。一个niladic函数是指一个没有输入的函数,表示如下:f:{[] … }例子如下:q){[] 42} / pure function returns constant 4242q){[] aa} / impure function: references global a{[] aa}函数最大的参数个数(valence)为8,超过8个参数将会报错。如果参数较多的话,将参数打包成list或者dictionary输入函数。函数的输出值为函数最后一个表达式的结果:q){[x] xx}q){[x;y] a:xx; b:yy; r:a+b; r}Keep it short. 很多q语言中的函数都是紧凑并且模块化的,很多函数都是仅仅一行3. 函数应用函数的调用是严格的,意味着在参数替换之前,表达式就已经被执行了:q)f:{[x] xx}q)f[0N!3+1]416当提供的参数多于函数定义的参数时,会报’rank错误。q) {[x] xx}[3;4]‘rank4. 没有返回值的函数函数体最后只有一个分号;,返回::。q)fvoid:{[x] a set x;}q)fvoid 42q)a42注意在q中,分号;是分隔符而并不是终止符。5. 参数的并列写法类似于列表索引和字典取值,函数的参数也可以是并列写法:q){[x] 2*x} 4284q)f:{[x] x*x}q)f 5256. 函数名的应用当函数被赋值给一个全局变量时,可以通过symbol形式的函数名来调用:q)f:{x*x}q)f[5]25q)f[5]25q)f 525q).my.name.space.f:{2*x}q).my.name.space.f[5]107. 隐含的参数当一个函数没有参数被明确定义时,三个隐含的参数x, y, z会被自动定义,下面的函数定义是等价的:{[x] xx}{xx}{[x;y] x+y}{x+y}三个参数x, y, z的调用是按照先后顺序的,意味着x总是第一个被调用,y第二个,z第三个。下面这个函数只有提供三个参数值时才会返回值q)g:{x+z} / likely meant x+y; requires 3 args in callq)g[1;2] / still waiting for 3rd arg – i.e., a projection{x+z}[1;2]q)g[1;2;3] / 2nd arg is required but ignored48. 匿名函数和lambda表达式一个没有函数名称的函数叫做匿名函数,匿名函数的常见的两个用处:定义在函数内的匿名函数f{[…] …; {…}[…]; …}函数容器q)powers:({1}; {x}; {xx}; {xxx})…q)selected:2q)powers[selected]{xx}9. 恒等函数::恒等函数::返回它的输入作为输出;裸的恒等函数不能使用并列的形式,它必须使用中括号调用。q)::[42]42q)::[abc]abcq):: 42 / error’q)(::) 424210. 函数是数据类似于python中的函数是对象一样,在q语言中函数都是数据,可以作为输入和输出。q)apply:{x y}q)sq:{xx}q)apply[sq; 5]252. 通过名字调用常规的函数使用传值调用(call-by-value),意味着参数在被传递时是按值传递的,在这个过程中,原始值被拷贝了一份,以保证之前的原始数据不会被修改。但这样做的一个问题是,当输入参数的size非常大时,拷贝是被禁止的。这个时候就有一个新的方法:call-by-name,在这种情况下变量的名字被传递而不是变量的值。Call-by-name没有特别的语法。一个例子就是内置函数get,传递全局变量的名字,返回对应的值q)a:42q)get a42另一个例子是函数set,是给全局变量赋值q)a set 43aq)a433. 局部和全局变量1. 定义局部和全局变量在函数体内使用:定义的变量被称为局部变量。函数体内最大的局部变量个数为24个。q)f:{a:42; a+x}q语言不遵循词法作用域规则,意味着在函数体内的函数并没有获得上层函数体内变量的权限,例如下例中, helper函数并没有获取局部变量a的值的权限q)f:{[p1] a:42; helper:{[p2] a*p2}; helper p1}然而,你必须对该局部函数声明一个额外的参数来传递局部变量q)f:{[p1] a:42; helper:{[a; p2] a*p2}[a;]; helper p1}q)f 5210在所有函数定义以外的变量被称为全局变量。2. 在函数内对全局变量赋值当函数体内没有同名的局部变量时,可以使用双冒号::来对全局变量进行赋值;q)b:6q)f:{b::7; xb}q)f[6]42q)b7而当函数体内有同名的局部变量时,双冒号符操作的是局部变量,并不是全局变量q)b:6q)f:{b:42; b::x; b}q)f[98]98q)b6相比::,更推荐使用set来进行全局变量修改, 这样就不会有局部变量名冲突了。q)a:42q)f:{a:98.6; a set x}q)f 43aq)a434. 投影投影是指只指定函数的一部分参数,其结果会是其余参数的函数。1. 函数投影一个函数投影的例子为:q)add:{x+y}q)add[42;]{x+y}[42;]q)add[42;][3]45q)add3:{x+y+z}q)add3[2][3][4]9上式可以理解为add3作用于参数2,返回一个函数;该函数作用于参数3,返回一个函数;最后作用于4,返回结果9。上式等价于add3[2;3;4]。No second look. 被赋值的函数投影变量不会随着原函数的改变而改变q)f:{x-y}q)g:f[42;]q)g{x-y}[42;]q)g[6]36q)f:{x+y}q)g{x-y}[42;]q)g[6]362. 运算符投影 当使用运算符中缀形式的时候,一个q的运算符可以通过固定其左运算元来进行投影,这时需要括号。q)(7*) 642由于任何操作符都是一个函数,当然可以使用其前缀形式进行投影:q)-[;42] 9856上面两个公式中的空格符都不是必须的:q)(7*)642q)-[;42]98563. 多维投影当函数有多个参数的时候(valence &gt; 2),函数可以有多个投影,例如下例q){x+y+z}[1;;3]{x+y+z}[1;;3]q){x+y+z}[1;;3] 265. Atomic函数一个Atomic函数是指该函数直接作用于一个q数据结构中的atom数据。1. Monadic Atomic函数和map回忆一下,monadic函数是指只作用于一个参数的函数,如下是一个monadic atomic函数的例子,该函数作用于一个字典q)neg 10-10q)neg 10 20 30-10 -20 -30q)neg (10 20 30; 40 50)-10 -20 -30-40 -50q)neg abc!10 20 30a| -10b| -20c| -30q)neg abc!(10 20; 30 40 50; 60)a| -10 -20b| -30 -40 -50c| -602. Dyadic atomic函数和zipDyadic函数是指作用于两个参数的函数。若将dyadic函数的非atomic部分固定下来(可以看成一个函数的投影),那么dyadic函数就变为monadic函数。如下例中的dyadic操作符?。q)10 20 30?100q)10 20 30?10 20 30 40 500 1 2 3 3q)(enlist 10)?100q)10 20?100q)10 20 30 40 50?100在算术中,比较和关系运算符都是atomic的,在这种应用下会有四种情况:atom和atomatom和listlist和atomlist和list在最后一种情况下,两个list元素的长度必须相等q)1+1011q)1+10 20 3011 21 31q)1 2 3+1011 12 13q)1 2 3+10 20 3011 22 33这个功能相当于传统语言中的zip3. 构造atomic函数简单想一想就可以明白,由atomic函数构成的函数仍然是atomic的,因此构造一个atomic函数的方法就是去包含内置的atomic函数。构造Monadic atomic函数:q)f:{(x*x)+(2*x)-1}q)f 0-1q)f til 10-1 2 7 14 23 34 47 62 79 98构造Dyadic atomic函数:q)pyth:{sqrt (x*x)+y*y}q)pyth[1; 1]1.414214q)pyth[1; 1 2 3]1.414214 2.236068 3.162278q)pyth[1 2 3; 1 2 3]1.414214 2.828427 4.2426416. 副词副词是高阶的函数,用以改变函数在列表上的应用方式。这个术语来自于将q操作符当做动词。Proficiency in the use of adverbs is one skill that separates q pretenders from q contenders. :)1. Monadic each合并函数例如count只会作用在嵌套列表的最高层级:q)count 10 20 303q)count (10 20 30; 40 50)2如果我们想知道嵌套列表中每个元素的长度,这个时候副词each就派上用场了,它使得monadic函数能够作用于列表的每个元素而不是整个列表,each有两种使用方法:中缀形式:each紧跟在函数的后面q) count each (10 20 30; 40 50)3 2前缀形式q) each[count] (10 20 30; 40 50)3 2对于层数较深的嵌套矩阵,可能需要对each进行迭代q)(count each) each ((1 2 3; 3 4); (100 200; 300 400 500))3 22 3q)each[each[count]] ((1 2 3; 3 4); (100 200; 300 400 500))3 22 3一些例子:q)reverse "live""evil"q)reverse ("life"; "the"; "universe"; "and"; "everything")q)reverse each ("life"; "the"; "universe"; "and"; "everything")当想要将一个长度为n的向量转换为一个大小为n*1的矩阵时,可以使用enlist each来实现,但flip enlist在大列表上执行更快。q)enlist each 1001 1002 1004 10031001100210041003q)flip enlist 1001 1002 1004 100310011002100410032. each-both'副词each-both符号'作用在一个dyadic函数上,使得函数能够成对地作用在对应的列表元素上,符号'读作"zip"。q)("abc"; "uv"),'("de"; "xyz")"abcde""uvxyz"q)1,'10 20 301 101 201 30q)1 2 3,'101 102 103 10q)2#'("abcde"; "fgh"; "ijklm")"ab""fg""ij"当熟练的时候,可以使用each-both的前缀形式:q),'[("abc"; "uv"); ("de"; "xyz")]"abcde""uvxyz"一个table的例子:q)t1:([] c1:1 2 3)q)t2:([] c2:abc)q)t1,’t2c1 c2—–1 a2 b3 c3. each-left :each-left操作符作用于一个dyadic函数,使第一个参数下的每一项都应用于第二个参数:(“abc”; “de”; enlist “f”) ,: “>““abc>““de>““f>“4. each-right /:each-right 作用于一个dyadic函数,使第一项作用于第二个参数的每一项:q)"</”,/:(“abc”;“de”;enlist “f”)"</abc”"</de”"</f"5. Cross Product叉积(Cross Product)成对地作用于左侧的每一项和右侧的每一项。 如果我们对join执行each-right和each-left操作,再内置函数raze对得到的嵌套矩阵夷平,就可以得到我们想要的结果q)1 2 3,/::10 201 10 1 202 10 2 203 10 3 20q)raze 1 2 3,/::10 201 101 202 102 203 103 20上述操作还是较为复杂,我们可以通过内置函数cross来得到上述结果q)1 2 3 cross 10 201 101 202 102 203 103 20可以注意到,若我们组合each-left和each-right时,我们便可以得到上述结果的转置q)raze 1 2 3,:/:10 201 102 103 101 202 203 206. Over /Over操作符/是一个提供递归机制的高阶函数。它最简单的形式是修改一个dyadic函数,使其在一个list上累积函数作用的结果q)0 +/ 1 2 3 4 5 6 7 8 9 1055注意: 在运算符和/之间不能存在空格,因为/可以被用作注释。运算符左运算元为累积计算的初始值,右运算元为待累积计算的列表。但我们也可以省略左运算元(即初始值),这个时候需要我们对表达式做一些变换,被作用函数和Over运算符/用括号包住,这时右边列表的第一个元素就是初始值。q)(+/) 1 2 3 4 5 6 7 8 9 1055上面的括号是必需项,被修改的函数实际上是一个monadic函数。一些有用的over形式:q)(/) 1 2 3 4 5 6 7 8 9 10 / product3628800q)(|/) 7 8 4 3 10 2 1 9 5 6 / maximum10q)(&/) 7 8 4 3 10 2 1 9 5 6 / minimum1使用,/可以高效地移除列表的顶层嵌套,其对应的内置函数为raze。q)(,/)((1 2 3; 4 5); (100 200; 300 400 500))1 2 34 5100 200300 400 500q)raze ((1 2 3; 4 5); (100 200; 300 400 500))1 2 34 5100 200300 400 5007. Iteration/的另一种用法是作为循环代码的等价形式。在这个版本下,左运算元表示了循环的次数,右运算元为初始值。例如,计算Fibonacci数列:q)fib:{x, sum -2#x}q)10 fib/ 1 11 1 2 3 5 8 13 21 34 55 89 144q)fib/[10;1 1]1 1 2 3 5 8 13 21 34 55 89 144另一个版本的/控制了循环的进行直至收敛,或者检测到一个环的存在。下面以牛顿法为例介绍这种循环的使用,我们使用牛顿法来寻找函数{-2+xx}的根:q)f:{-2+xx}q)secant:{[f;x;e] (f[x+e]-f x-e)%2e}q){x-f[x]%secant[f; x; 1e-6]}/[1.5]1.414214q语言会判断当前的输出值与之前的输出值之间的大小,如果两个值之间相差在一定的tolerance以内,则认为算法收敛并且迭代完成;否则继续执行循环任务。此外,q语言通过每次比较运算结果和初始值是否match()来判断当前程序是否存在环(loop),如果存在环,程序则终止:q)newtcycle:{[xn] xn-((xnxnxn)+(-2xn)+2)%-2+3xnxn}q)newtcycle/[0.0]1f如果运算的结果与初始值相等,但是类型不同(not match),程序则不会停止;例如上例中如果提供初始值0,则程序会一直运行下去。运算符/的最后一种重载用法,等价于使用while循环,它提供了一个判断条件,若每次的运算结果满足条件,则继续执行;否则,终止计算q)fib:{x,sum -2#x}q)fib/[{1000>last x}; 1 1]1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 15978. Scan \Scan操作符\同样是一个高阶函数,其作用与操作符/一样,与其不同的是,Scan\会返回中间的计算结果。可以把Scan\当做是Over/的"running"版本。q)0+\1 2 3 4 5 6 7 8 9 101 3 6 10 15 21 28 36 45 55q)()1 2 3 4 5 6 7 8 9 10q)(|)7 8 4 3 10 2 1 9 5 67 8 8 8 10 10 10 10 10 10q)(&)7 8 4 3 10 2 1 9 5 67 7 4 3 3 2 1 1 1 1q)100 f\1 2 3 4 5 6 7 8 9 10q)(f)1 2 3 4 5 6 7 8 9 10所有over操作符/的用法都适用于Scan操作符\,使用Scan操作符的好处是可以看到函数中间过程的运行结果。q)fib:{x, sum -2#x}q)fib[{1000>last x}; 1 1]9. each-previous ‘:each-previous操作符对列表的每项和其前一项执行dyadic函数操作。当执行Dyadic操作时,当前项是Dyadic函数的左运算元,前一项是Dyadic函数的右运算元。由于列表中第一项没有前项,所以我们必须在运算符:‘左运算元的位置提供初始值,如下例q)100 -’: 100 99 101 102 1010 -1 2 1 -1与其它副词一样,each-previous :‘也有一个monadic函数的形式。但在这种形式下,列表中的第一个元素不会被当做初始的前项,相反,它直接返回该项。q)(-’:)100 99 101 102 101100 -1 2 1 -1q)deltas 100 99 101 102 101100 -1 2 1 -1q)(%’:)100 99 101 102 101100 0.98999999999999999 1.0202020202020201 1.0099009900990099 0.99019607843137258q)ratios 100 99 101 102 101100 0.98999999999999999 1.0202020202020201 1.0099009900990099 0.99019607843137258保留第一项的动机是,可以通过保留的第一项来恢复整个listq)sums deltas 100 99 101 102 101100 99 101 102 101q)deltas sums 100 99 101 102 101100 99 101 102 101当我们需要返回的结果中都是变化值时,可以通过如下方法得到q)deltas0:{first[x] -’: x}q)deltas0 100 99 101 102 1010 -1 2 1 -1一个使用each-previous的非常有用的工具是使用判断连续项是否是match的。实际中,我们经常会关注两个连续项不同的情况,这种情况下使用内置函数differq)(’:) 1 1 1 2 2 3 4 5 5 5 6 6011010001101bq)not (’:) 1 1 1 2 2 3 4 5 5 5 6 6100101110010bq)differ 1 1 1 2 2 3 4 5 5 5 6 6100101110010b可以对differ的结果使用where和cut来分隔列表q)L:1 1 1 2 2 3 4 5 5 5 6 6q)where differ L0 3 5 6 7 10q)(where differ L) cut L1 1 12 2,3,45 5 56 6下面我们来做一些q的练习q)runs:(where differ L) cut L / store runsq)ct:count each runs / store count of each runq)runs where ct=max ct / find the runs of maximum length1 1 15 5 5用一行代码来实现上面的代码q) runs where ct=max ct:count each runs:(where differ L) cut L同样,我们可以上述技术来找到上升和下降子序列q)L:9 8 7 11 10 12 13q)(where -0W>’:L) cut L9 8 711 10,12,13q)(where 0W<’:L) cut L,9,87 1110 12 138. 一般应用Thorough understanding of the general application is another test that separates the q pretenders from the contenders.1. 动词 @q语言的基础操作包括:从list中通过索引取值,在字典中通过键取值或执行一个monadic函数。高阶函数@是q语言中基础操作的真正形式,它将一个monadic映射(可能是索引取值,字典取值或者monadic函数)作用于一个元素之上。与所有的内置函数一样,它同样有中缀和前缀的表示形式q)10 20 30 40@120q)L:10 20 30 40q)L@120q)@[L; 1]20q)count@L4q)@[count; L]4q){x*x}@L100 300 900 1600q)d:abc!10 20 30q)d@a10q)@[d;b]20当@与niladic函数应用时,可以使用空元素::来代表空值q)f:{6*7}q)f[]42q)@[f; ::]42q)f@(::)42q)f@43422. 动词.q语言中,多元映射包括了:深度索引一个列表,从一个字典中取一个被嵌套的值和执行一个带有多个参数的函数等。高阶函数.是q语言中多元应用的真正形式。它将多元映射投影到多个参数上,并且可以被写为中缀和前缀形式。.的右侧必须是一个listq)L:(10 20 30; 40 50)q)L[1][0]40q)L[1; 0]40q)L . 1 040q)d:abc!(10 20 30; 40 50; enlist 60)q)d[b][0]40q)d[b; 0]40q)d . (b; 0)40q)g:{x+y}q)g[1; 2]3q)g . 1 23可以配合monadic函数使用.,其效果如下q)f:{x*x}q)f@525q)f . enlist 525q)f . enlist 1 2 31 4 9为了表示一个隐藏的索引,可以使用::来代替q)m:(1 2 3;4 5 6)q)m[0;]1 2 3q)m . (0; ::)1 2 3q)m . (::; 1)2 5对于一个niladic函数的.执行形式,需要使用::生成一个list。q)f:{6*7}q)f . enlist (::)42q)f . enlist 4242All data structures in q are composed from lists and dictionaries.一些很好的练习:q)L:10 20 30q)L . enlist 1_q)m:(10 20 30; 100 200 300)q)m . 0 1_q)ds:(abc!10 20 30; xy!100 200)q)ds . (0; b)_q)mix:(10 20 30; abc!(1; 2; (300 400)))q)mix . (1; c; 1)_q)dc:c1c2!(1 2 3; abc)q)dc . (c2; 1)_q)t:([]c1:1 2 3;c2:abc)q)t . (1; c2)_答案分别是20 20 20 400 b b3. 应用Monadic函数的@回忆@的一般操作:q)L:10 20 30 40 50q)@[L;1]20q)@[L;0 1]10 20现在除了取值外,我们同时应用一个函数:q)@[L;1;neg]10 -20 30 40 50q)@[L;0 2;neg]-10 20 -30 40 50注意到上述结果与正常在列表子集上的运算不同,正常只会返回在子集上运算的结果q)neg L@0 1-10 -20而这个提升的版本会返回修改后的整个列表。Monadic函数使用@的一般应用 的语法是@[L;I;f]其中L是列表,I为索引的容器。这个形式可以泛化到任何可以被视为映射的数据结构,例如给定一个字典和一个键值列表q)d:abc!10 20 30q)ks:acq)@[d; ks; neg]a| -10b| 20c| -30上述操作都是在输入数据结构的拷贝上完成的。我们也可以通过pass-by-name的方法来进行in-place修改q)L:10 20 30 40q)@[L; 0; neg]-10 20 30 40q)L10 20 30 40q)@[L; 0 ; neg]Lq)L-10 20 30 404. 应用Dyadic函数的@当Dyadic函数使用@时,需要提供一个额外的运算元,显然运算元要与子集的大小匹配。除了一种额外的情况,当运算元是atom时,会被自动拓展到与子集相同大小。q)L:10 20 30 40q)@[L; 0 1; +; 100 200]110 220 30 40q)@[L; 0 1; +; 100]110 120 30 40q)d:abc!10 20 30q)@[d; ab; +; 100 200]a| 110b| 220c| 30q)@[d; ab; +; 100]a| 110b| 120c| 30Dyadic函数使用@的一般应用 的语法是@[L;I;g;v]其中L和I与上小节定义一样,可以是任意能够被视为映射的数据结构;g是一个Dyadic函数;v是一个atom或者与I匹配的列表。列表赋值: 一个非常有用的Dyadic函数应用是使用赋值符:在子集上赋值q)L:10 20 30 40q)@[L; 0 2; :; 42 43]42 20 43 40与Monadic函数一样,in-place操作可以通过pass-by-name形式q)L:10 20 30 40q)@[L; 0 2; :; 42 43]Lq)L42 20 43 405. 应用Monadic函数的.总结一下,@是作用在数据结构的顶层,而.则是深度索引。重新回顾一下.的前缀用法q)m:(10 20 30; 100 200 300)q).[m; 0 1]20q)d:abc!(10 20 30; 40 50; enlist 60)q).[d; (a; 1)]20应用monadic函数的.形式:q).[m; 0 1; neg]10 -20 30100 200 300q).[d; (a; 1); neg]a| 10 -20 30b| 40 50c| ,60同样,若想in-place修改,则使用pass-by-name形式。可以使用::来代替隐藏的索引q).[m; (0; ::); neg]-10 -20 -30100 200 300q)d:abc!(100 200 300; 400 500; enlist 600)q).[d; (a; ::); neg]a| -100 -200 -300b| 400 500c| ,600q).[d; (::; 0); neg]a| -100 200 300b| -400 500c| ,-600应用monadic函数的.一般形式为:.[L; I; f]7. 应用Dyadic函数的.一般形式为.[L;I;g;v]其中g是Dyadic函数,v是atom或与I相对应的运算元q)m:(10 20 30; 100 200 300)q).[m; 0 1; +; 1]10 21 30100 200 300q).[m; (::; 1); +; 1 2]10 21 30100 202 300q)m10 20 30100 200 300q).[m; (::; 1); +; 1]mq)m10 21 30100 200 300q).[m; (::; 1); :; 42]mq)m10 42 30100 42 300q)d:abc!(100 200 300; 400 500; enlist 600)q).[d; (a; 1); +; 1]q).[d; (a; ::); +; 1]q).[d; (::; 0); +; 1]q).[d; (::; 0); :; 42] ...

April 3, 2019 · 6 min · jiezi

5. Q语言学习之路—字典

字典基础1. 定义字典是key-values pairs, 但是在q中,字典是按照list来存储的。字典的创建使用操作符!,读作bang :) 所有的字典类型都是99h。q)10 20 30!1.1 2.2 3.310| 1.120| 2.230| 3.3q)abc!100 200 300a| 100b| 200c| 300可以使用操作符key, value, count来分别获取字典的键、值和个数。尽管q语言不强制键的唯一性(historical mistake),但是却对每个输入值都提供唯一的输出,只有第一次出现的key会被看到。当你知道字典的键是unique的时候,可以使用命令u#将字典转换为哈希表,这样与原来的线性搜索相比,会提高查找的速度。q)(u#abc)!10 20 30注意:与传统语言不一样,不同顺序的字典在q中是不等的。q)(abc!10 20 30)~acb!10 30 200b2. 空和单例字典 空字典:()!()带类型的空字典:symbol$()!float$()单例字典,必须要使用enlist形式先生成一个list,否则会报错q)(enlist x)!enlist 42x| 423. 查找使用中括号或者并列的形式:q)d:abc!10 20 30q)d[a]10q)d b20当查找值不在字典的键表中时,返回值列表中初始值类型的Null值:q)d[x]0N4. 逆向查找 使用? 与列表类似,?返回值在字典中对应的键。q)d:abca!10 20 30 10q)d?10a当查找的值不在字典的值列表中时,返回键列表中初始值类型的Null值:q)d:abca!10 20 30 10q)d?405. 字典和列表 可以通过字典来表示一个稀疏列表:q)d1:0 100 500000!10 20 30q)d2:0 99 1000000!100 200 300q)d1+d20 | 110100 | 20500000 | 3099 | 2001000000| 3006. 不唯一的键和值 如之前所说,当键不唯一时,会返回第一个出现时的值:q)ddup:abac!10 20 30 20q)ddup[a]10逆向查找同理:q)ddup?30aq)ddup?20b7. Non-simple 键 和 值键和值是嵌套的列表:q)d:(ab; cde; enlist f)!10 20 30q)d f30q)d?20cdeq)d:abc!(10 20; 30 40 50; enlist 60)q)d b30 40 50q)d?30 40 50bq)d?enlist 60注意,对单个元素的键或值要单独对其生成一个列表(enlist),否则会有如下的问题:q)dwhackey:(1 2; 3 4 5; 6; 7 8)!10 20 30 40 / atom 6 is whackq)dwhackey 1 210q)dwhackey 60Nq)dwhackval:10 20 30 40!(1 2; 3 4 5; 6; 7 8) / atom 6 is whackq)dwhackval?3 4 520q)dwhackval?60N会导致查找失败,返回Null值。2. 字典操作1. Amend 和 Upsert update:q)d:abc!10 20 30q)d[b]:42insert:q)d:abc!10 20 30q)d[x]:42在q语言中,update/insert操作被称为upsert操作。2. 提取子字典使用提取操作符#,左运算元为子字典的键,右运算元为原始字典。 另外,当原始字典有重复键时,只会提取第一次出现的键:q)ddup:abac!10 20 30 20q)ac#ddupa| 10c| 203. 删除 使用操作符_, 用法与#类似,但需要注意_前后要加空格。q)d:abc!10 20 30q)ac _ db| 20q)(enlist b) _ d删除全部键值对会得到一个带类型的空字典:q)d:abc!10 20 30q)abc _ dq)-3!abc _ d"(symbol$())!long$()“操作符cut与_在字典上的效果相同。另外一个很少用的用法是,字典在_的左边,_右运算元为一个单一的键,表示字典删除这个键:q)d _ ba| 10c| 304. 字典上的基础运算一些常见的基础运算如下例所示,非常容易理解q)d:abc!10 20 30q)neg da| -10b| -20c| -30字典的加法: 相同键值进行加法操作,不同键值保留下来q)d1:abc!1 2 3q)d2:bcd!20 30 40q)d1+d2a| 1b| 22c| 33d| 405. join,使用,进行合并字典的操作,由于q语言是right to left特性的,所以合并的两个字典如果有相同的键值,那么右边的会保留下来。q)d1:abc!10 20 30q)d2:cd!300 400q)d1,d2a| 10b| 20c| 300d| 400因此,字典合并时,前后顺序很重要。6. Coalesce ^ 这种合并方式类似于,合并, 但与其不同的是,当^右边右边项值为Null时不覆盖左边项,见下例:q)d1:abc!10 0N 30q)d2:bcd!200 0N 400q)d1^d2a| 10b| 200c| 30d| 400q)d1,d2a| 10b| 200c|d| 4007. 算术和相等运算符 对于相等性比较,由于null代表缺失值,所以所有的null值会被认为是相等的。q)(abc!10 20 30)=bcd!20 300 400a| 0b| 1c| 0d| 0q)(abc!0N 20 30)=bcd!20 300 0Na| 1b| 1c| 0d| 1q)(abc!10 20 30)<bcd!20 300 400a| 0b| 0c| 1d| 13. 列字典列字典(Column Dictionary)是表(Table)的基础。1. 定义和术语一个一般的列字典具有如下的形式:c1...cn!(v1;...;vn)其中ci是symbol类型,vi是具有相同长度的列表。通常vi均为简单列表。2. 简单例子q)travelers:nameiq!(DentBeeblebroxPrefect;42 98 126)q)travelersname| Dent Beeblebrox Prefectiq | 42 98 126索引:q)travelers[name; 1]Beeblebroxq)travelers[iq; 2]126单列的字典:q)dc1:(enlist c)!enlist 10 20 30q)dc1c| 10 20 304. 列字典的翻转q)dc:c1c2!(abc; 10 20 30)q)dcc1| a b cc2| 10 20 30q)t:flip dcq)tc1 c2-----a 10b 20c 30索引:q)dc[c1; 0]aq)dc[c1; 1]bq)dc[c1; 2]cq)t[0; c1]aq)t[1; c1]bq)t[2; c1]`cUnlike the case of transposing rectangular lists, transposing a column dictionary does not physically re-arrange data.(翻转并不会改变数据的存储)

March 30, 2019 · 2 min · jiezi