函数的介绍
调用函数的时候用[]来分隔函数名和参数,在参数列表中用;分隔各参数,如f[x;y]。操作符实际上也是函数,只不过函数通常写法是函数名+参数,而操作符是介于操作数中间。
Verb:二元操作符,例如2+3中的+操作符,verb也可以使用通常的函数表达式,如+[2;3],甚至混合使用,如(2+)[3],(2+)3也是可以的。
基本操作符:内建的二元原子操作符(操作原子数据类型),包括数值计算、关系、比较操作符。
列表的智能处理
某些智能的原子操作函数或者操作符不仅能操作原子数据类型,当参数是列表的时候,它们能将操作应用到列表上的每一个元素上。下面列举了当其中一个或多个参数是列表时返回值列表的第i个元素的值
操作 | 返回值的第i个元素的值 |
m[L] | m[L[i]] |
a op L | a op L[i] |
L op a | L[i] op a |
L1 op L2 | L1[i] op L2[i] |
这个特点很重要,尤其是对于从传统的编程语言(Java/C/C++/C#等)转向KDB编程的人。例如KDB对表操作实际上是对每个列进行操作,但在q代码里很多时候(使用内建操作符、函数)看不出来,当你使用自定义函数的时候就会遇到麻烦。
要注意的是,如果参数都是列表,这种智能处理一般要求这些列表的长度一致。
q)1 2 3=1 2 3 111b q)1=1 2 3 100b q)" ">"abc" 000b q)" ">"abc" 'length
操作符优先级
q没有定义任何优先级,表达式的计算顺序是从右到左。括号可以用于改变优先级。
不定义操作符优先级的原理
如果定义了操作符优先级,表达式的所有元素需要在执行前先分析一遍以找出执行顺序(效率低?),而且经常需要用括号来改变优先级。另外如果一个语言支持将二元函数当做操作符,例如f[x;y]可以写作x f y,那么还需要修改操作符优先级以支持这个特性,这不仅不切实际,还会导致更多的括号。
Match (~)
用于比较两个实体。相等的实体有相同的shape(结构?),类型,值(注意不同类型的不能相等,如42i ~ 42h返回0b),但可能占用不同的存储位置(有点类似Java两个对象是相等的--equals,但不一定是同一个对象--==)。
关系操作符
关系操作符操作的是原子类型。
等于(=)和不等于(<>)
=和~的区别:
- =只操作原子数据类型
q)L1:1 2 3 q)L2:1 2 3
- 对于数值和字符类型,=不需要相同的数据类型,symbol只能跟symbol比较
q)42=42.0 1b q)`4="4" 'type q)`42=`42 1b
<>相当于not =
q)a:42 q)b:98.6 q)a<>b 1b q)not a=b 1b
注意比较浮点数的时候,会有一个允许的乘法误差,例如
q)r:1%3 q)r 0.3333333 q)2=r+r+r+r+r+r 1b
非0(not)
not只能操作数值、字符类型和时间类型,不能用于symbol。not其实就是一个判断是不是0的操作(时间类型用not操作符其实没有太大意义,2000年1月1日0时整和00:00:00.000代表是0,其他日期或者时间not返回值都是0b)
q)not 0 1b q)not 1 0b q)not 42 0b q)not `0 'nyi
排序(<,<=,>,>=)
- 数值和字符的比较就是看它们的数值(字符的数值是ASCII码)
- Symbol的比较是按字典顺序(我猜测是根据每个字母去比较,首字符跟首字符比较,第二个字符跟第二个字符比较,以此类推,空小于任何非空;字符间的比较应该还是按ASCII码)
q)`a>=`b 0b q)`ab>`abc 0b q)`1>`2 0b q)`>`0 0b
列表的智能处理
虽然我们说关系操作符操作的是原子类型,事实上写代码的时候我们可以操作列表,这是因为这些操作符会自动处理列表。注意原子类型跟列表是拿原子类型跟列表的每个元素比较,列表跟列表比较是拿每个同位置元素去比较,最后返回的也是一个列表。如果两个都是列表但是长度不一样的话会返回`length错误
q)1 2 3=1 2 3 111b q)1=1 2 3 100b q)" ">"abc" 000b q)" ">"abc" 'length
基本算术运算符+-*%
/用于注释所以用%表示除法。
除除法外(除法的返回值永远是浮点数),返回值的类型决定于操作数,精度低的类型会被提升为精度高的(二进制数据会被转换为int)。计算结果不会被自动转换,例如溢出会被忽略
q)i:2147483647i q)i+3i -2147483646i
注意这些算术运算符都是二元的,-可以用于代表负数,但是它不能作为操作符把一个值转换成负数,可以用neg来转换为负数
q)a:-4 q)a -4 q)-a '- q)neg a 4
Max(|)和Min(&)
这两个操作符是原子数据操作符和二元的,返回值类型是操作数中精度较高的类型。数值类型和字符类型可以一起比较,symbol类型无法参与这种比较。|也可以写作or,&可写作and
要注意的是这两个操作符跟Java中的含义不一样,Java里它们是逻辑操作符,返回值是true或者false;q里返回值是两个操作数的最大值或者最小值,不一定是boolean值,虽然它们经常用于boolean判断。
q)0b|1b 1b q)42|42.1 42.1 q)"a"|"z" "z" q)`a|`z 'type
指数操作符:sqrt、exp、log、xexp、xlog
这些操作的返回值都是浮点数,如果遇到不合法的情况返回的是浮点数空值(0n,不是0,浮点数0是0f),例如对负数开方等。
- sqrt:开平方
- exp:自然对数的底数(e)的幂
- log:自然对数
- xexp:幂,二元操作,表示一个数的几次方
- xlog:对数,二元操作
其他基本操作符:mod、signum、reciprocal、floor、ceiling、abs
- mod:取模
- signum:正负号函数,返回1i(正数)、-1i(负数)和0i(0)
- floor:向下取整,只能用于int、long和浮点数,不能用于boolean、byte、short
- ceil:向上取整,只能用于int、long、boolean、byte和浮点数,不能用于short
- abs:取绝对值
时间数据操作
时间数据的内部形式
时间数据实际上是以有符号的浮点数存储的
- 对于date和datetime来说,0.0表示2000年1月1日0时整。整数部分表示日期相对于2000年1月1日的天数偏差,小数部分表示相对于1整天的比例,例如中午12点整是0.5
- 对于time来说,它是从一天0时整开始的毫秒数
基本操作
date/datetime之间的操作基于它们所代表的浮点数,而time之间的比较基于它们代表的毫秒数。
date和time可以相加得到一个datetime
q)2007.07.04+12:45:59.876 2007.07.04D12:45:59.876000000
当时间数据与数值类型比较时,取其代表的浮点数来进行计算。
当时间数据与数值类型进行加减操作时,相当于浮点数计算然后将结果转换为时间类型,例如date/datetime类型-2相当于减去两天。注意时间超出24小时的时候结果是这样的
q)23:59:59.999+2 24:00:00.001
当时间数据之间进行减法操作时,取其代表的浮点数来计算,结果可以认为是天数或者毫秒数。
无穷数和空的操作
产生无穷数
非零数除以0即可得到float类型的无穷数(0w或者-0w)。int的无穷数不能通过除0得到,因为除法的返回值只能是浮点数。
产生NaN
0除以0即可得到NaN(浮点数的空:0n)
无穷数和空的基本算术操作
如果表达式的一个成员是无穷数或者空,则结果就是无穷数或者空,其中NaN>>空>>无穷数。注意正负无穷数所带的正负号对结果仍然有影响。例外的是正无穷整数和负无穷整数相加的结果是0的整数。
类型提升
在混合类型的表达式中,空值的类型提升跟有穷数的规则一样。
相等性
无穷数不等于大多数数值,包括空。所有的空都相等,因为它们的区别只是类型。整数类型的无穷数其实是用该类型的最大值存储的,因此它们等于该类型的最大数,如
q)32767=0Wh 1b q)-32767=-0Wh 1b
Match
Match跟相等不一样,因为match会比较类型。拿整数类型的无穷数跟同类型的最大值比较,结果也是1b(注意同类型)
q)-32767h~-0Wh 1b q)-32767~0Wh 0b
not
非零操作对于所有的无穷数和空都是返回0b。
neg
无穷数有正负号,所以neg操作跟普通数值一样。空值没有正负号,因此返回还是自身。
比较
空值< -0wf < -0Wj < -0Wi < -0Wh < 非无穷数数值 < 0Wh < 0Wi < 0Wj < 0wf。空symbol小于任何其他symbol。
记住每个类型的无穷数都是该类型的最大值,所以long无穷数>int无穷数>short无穷数。
Max和Min
max和min操作依据上面的相等和比较操作结果而定。要注意的是,如果包含无穷数的表达式中出现了类型提升,那么结果可能不是无穷数。
q)0Ni&100f 0n q)0Wi&0Wj 2147483647
别名(alias)
别名是包含了其他变量的表达式。
别名和Double assignment
::是double assignment。它用于定义别名。别名的值不是赋值的时候确定的,而是动态的根据其对应的表达式的值变化而变化。别名跟普通变量的区别就是普通变量是固定值的(赋值后不再变化,除非重新赋值),而别名的值是动态的(赋值之后随所定义的表达式的值变化而变化)。
q)a:42 q)b::a q)c:a q)b 42 q)c 42 q)a:98.6 q)b 98.6 q)c 42
别名的作用类似于一个函数,别名更加方便,但别名容易引起误解,函数更加明显的定义了依赖关系。
别名也可以引用别名从而组成一条链。但不允许封闭的依赖链。
q)a:42 q)b::a q)c::b+1000 q)b 42 q)c 1042 q)a:98.6 q)b 98.6 q)c 1098.6 q)a::c 'loop
别名还可以用于产生数据库的视图(注:原书上的代码用:定义这个视图,应该是错误的,应该用::--也许是因为版本变更所致,也许是原书错误--,如下所示)
q)t:([]c1:`a`b`c`a;c2:20 15 10 20;c3:99.5 99.45 99.42 99.4) q)va:select sym:c1,px:c3 from t where c1=`a q)va sym px -------- a 99.5 a 99.4 q)va2::select sym:c1,px:c3 from t where c1=`a q)va2 sym px -------- a 99.5 a 99.4 q)update c3:100.0 from `t where c1=`a `t q)t c1 c2 c3 ----------- a 20 100 b 15 99.45 c 10 99.42 a 20 100 q)va sym px -------- a 99.5 a 99.4 q)va2 sym px ------- a 100 a 100
注意,::还有设定为全局范围的作用,请参考global amend
依赖关系
.z.b可以返回依赖列表。原书所说视图的依赖关系不显示在这个列表里应该也是错误的,怀疑是作者并没有用::实际产生一个视图导致。
q).z.b a| b b| c t| va2