0. "Operators and Verbs Are Functions"
在q中,操作符(Operators)又称为动词(Verbs), 读表达式3+2
按照从右到左的顺序:3 被加到 2,其中3是一个名词(主语),操作符+
是一个动词,2是一个名词(宾语)。
1. 函数标识
下面介绍三种以后常见的函数分类:
monadic函数: f[x]
或者 f x
dyadic函数: g[x;y]
或者 x g y
atomic函数: 作用于数据结构的那个元素
例如+
是一个dyadic函数,它有如下两种表达方法,都是等价的:
q) 2+3
q) +[2;3]
第二个式子很神奇,类似的表达方式还有:
q)=[2;3]
0b
更神奇的是,可以将二元运算符的前缀和中缀组合在一起:
q)(2+)[3]
5
q)(2+)3
5
2. atomic函数的拓展
用法见如下几个例子, 比较容易理解:
q)neg 1 2 3
-1 -2 -3
q)1 2 3+10 20 30
11 22 33
q)1 2 3+10 20 30 40
'length
q)100+1 2 3
101 102 103
q)1 2 3+100
101 102 103
1. 运算符优先级
没有运算符优先级!
1. Left-of-Right解读方法
由于q语言没有运算符优先级,但是有一个简单的法则来解读任何表达式:
Expressions are evaluatedleft-of-right
which equates to
Expressions are evaluatedright-to-left
类似于解读复合函数f(g(x))
, 可以被解读为f of g of x
, 同样可以被解读为x to g to f
。
注意:
当表达式的结果是中缀运算符的左操作元时,必须对该表达式加括号
,否则中缀运算符会作用在表达式最右边的一个元素上。如下例所示:
q)2*3+4
14
q)(2*3)+4
10
q)4+2*3
10
2. 没有运算符优先级的原因
- 运算符优先级的开销较大,只有当解析完整个表达式后才能开始计算
- 操作符的优先级往往会被括号所覆盖
- 一些编程语言允许用户自定义dyadic运算符,这就需要拓展运算符的优先级别来cover用户定义的运算符,这就导致了复杂性。
2. Match ~
作用于任意两个q元素,当两个元素相同(identical)时返回1b
, 不同时返回0b
。对于相互match的两个元素,它们需要同样的类型,同样的大小,同样的值, 但也可能占据不同的储存空间,这也意味着,拷贝项在q中被认为是相同的。
q)42~40+2
1b
q)42~42h
0b
q)42f~42.0
1b
q)42~`42
0b
q)`42~"42"
0b
q)4 2~2 4
0b
q)42~(4 2;(1 0))
0b
q)(4 2)~(4;2*1)
1b
q)(())~enlist ()
0b
q)(1; 2 3 4)~(1; (2; 3; 4))
1b
q)(1 2;3 4)~(1;2 3 4)
0b
3. 相等和关系运算符
1. 相等=
和不等<>
相等运算符=
和Match运算符~
不同之处在于,相等运算符=
是atom-wise
的,即atomic
函数。
相等运算符校验的是两个元素是否是值相等的,并不管元素的类型:
q)42=42i
1b
q)42=42.0
1b
q)42=0x42
0b
q)42="*"
1b
最后一项说明了,char "*"
的underlying值和42
的underlying值是一样的。
但对于日期类型,比较的是时间上的先后关系,而不是其underlying值
q)2000.01.01=2000.01.01D00:00:00.000000000
1b
q)2015.01.01<2015.02m
1b
q)12:00:00=12:00:00.000
1b
对于float元素的对比,q语言的容限是10^-14
。
2. 非零 not
如果对应元素的underlying值是0,则返回1b;否则返回0b。
对于char类型,"\000"
为0;对于时间类型,千禧年0时刻的值为0。
3. 大小关系符 >, <=, >, >=
对于char类型和numeric类型的比较,比较的是其underlying的数值。
symbol的比较按照字典序:
q)`a<`b
1b
q)`abc<`aba
0b
4.基础数学运算符 +, -, *, %
与其它编程语言不同的是,在q语言中使用%
而不是/
代表除法,因为/
被用来作为注释的分隔符,而且q god
认为%
更接近与除号÷
。 :)
除号返回的结果总是float
类型。
5. 最大|
和最小&
|
返回左右运算元的最大元素,对于二元数据来说,可以简化为逻辑运算符or
。&
返回左右运算元的最小元素,对于二元数据,简化为and
。
q)42|43
43
q)0b|1b
1b
q)1b&0b
0b
q)42|0x2b
43
q)"a"|"z"
"z"
q)`a|`z / error
'type
|
和&
操作同样是item-wise
的,如下例:
q)2|0 1 2 3 4
2 2 2 3 4
q)11010101b&01100101b
01000101b
q)"zaphod"|"arthur"
"zrthur"
对于二元数据的可读性,|
可以被写为or
,&
可以被写为and
。
q)42 or 43
43
6. 修订符(Amend) :
一个对:
的重载是inplace 赋值
q)a:42
类似与C语言中的+=,-=
等,在q语言中,同样有+:, -:, &=
,均表示inplace赋值
即使变量尚未被创建,也可以使用amend形式:
q)x
'x
q)x+:42
q)x
42
一个非常有用的形式是,:
,对list进行inplace的append操作:
q)L:1 2 3
q)L,:4
q)L
1 2 3 4
Amend会自动做类型提升,除了,:
q)L:1.1 2 2 3.3
q)L[1]+:100
q)L,:100
'type
7. 指数基元: sqrt, exp, log, xexp, xlog
sqrt
和exp
与传统编程语言相同,log
则是以自然对数e
为底的。
xexp
代表乘方, xlog
代表以左运算元为底的对数
q)2 xexp 5
32f
q)-2 xexp .5
0n
q)2 xlog 32
5f
q)2 xlog -1
0n
8. 更多的数学运算基元
1. 商div
和 余数mod
div
的结果是向下取整, mod
的计算公式是dividend – (dividend div divisor)
q)7 div 2
3
q)7 div 2.5
2
q)-7 div 2
-4
q)7 mod 2.5
2
q)-7 mod 2
1
q)7 mod 2 3 4
1 1 3
2. 取符号signum
其结果返回1i
代表正,-1i
代表负,0i
代表0.
3. 倒数 reciprocal
q)reciprocal 0.02380952
42.00001
q)reciprocal 0.0
0w
q)reciprocal -0.0
-0w
4. floor
与 ceiling
floor
向下取整, ceiling
向上取整, 用floor
可以规整浮点数类型的位数
q)x:4.242
q)0.01floor 100x
4.24
For reasons known only to the q gods, floor and ceiling do not apply to short types.
q)floor 4h
'type
9. 时间类型的操作符
对同种时间类型的数据比较是针对其underlying的数值进行的;对于不同时间类型的数据,q会先将他们转换到同种类型再对其underlying值作比较。
一些常见的操作:
q)2015.01.01+12:00:00.000000000
2015.01.01D12:00:00.000000000
q)2015.01.01D00:00:00.000000000-2014.01.01D00:00:00.000000000
365D00:00:00.000000000
q)12:00:00-11:00:00
1:00:00
q)12:00-11:00
1:00
10. 对inf
和null
的操作
float和int的inf
对应的二进制表示如下:
Value Bit Representation
0Wh 0111111111111111b
-0Wh 1000000000000001b
0Wi 01111111111111111111111111111111b
-0Wi 10000000000000000000000000000001b
0W 0111111111111111111111111111111111111111111111111111111111111111b
-0W 1000000000000000000000000000000000000000000000000000000000000001b
所有的null
值都相等(=
),因为它们都代表缺失值,但并不match
(~
),因为类型不同。
NaN
值都相等,并且not
对所有的null
值和inf
值都返回0b
,因为它们都不等于0.
q)not 0W
0b
q)not -0w
0b
q)not 0N
0b
对于任何数值类型:null < negtative infinity < normal values < positive infinity
对于inf
值的大小,取决于他们类型的宽度,对于正无穷: short < int < long < real < float
, 对于负无穷:-float < -real < -long < -int < -short
q)0W<0w
1b
q)-0w<0W
1b
q)-10000000<0N
0b
q)0N<42i
1b
q)0n<-0w
1b
11. 别名(Alias) ::
一个alias
是一个表达式——它并不是表达式的结果,而是表达式本身。
1. 创建别名::
如下b
是a
的别名
,当a
改变时,b
的值也跟着改变,但c
不会改变。
q)a:42
q)b::a
q)c:a
q)a:43
q)b
43
q)c
42
下面是一个更有趣的例子:
q)w::(x*x)+y*y
q)x:3
q)y:4
q)w
25
q)y:5
q)w
34
注意:只有当alais
所依赖的变量发生变化时,才会被重新计算(re-evaluated)。
2. 别名 vs. 函数
我们可以定义函数如下
q)fu:{(x*x)+y*y}
q)fu[3;4]
25
别名和函数的区别在于:
- 函数需要提供明确的参数;而对别名,你可以在程序的任意地方对变量赋值,而且当且仅当别名被引用时,表达式才会被计算。
- 函数并不保存计算结果,而别名保存计算结果。
3. 依赖关系别名
依赖其表达式中的变量。其依赖关系储存在系统字典中,可以通过命令.z.b
或者命令\b
来获取。
q)w::(x*x)+y*y
q).z.b
x| w
y| w
4. 视图 view
别名常被用来创建一个数据库的视图:
q)t:([]c1:`a`b`c`a;c2:20 15 10 20;c3:99.5 99.45 99.42 99.4)
q)v::select sym:c1,px:c3 from t where c1=`a
q)v
sym px
--------
a 99.5
a 99.4
q)update c3:42.0 from `t where c1=`a
`t
q)v
sym px
------
a 42
a 42
表的依赖项可以通过.z.b
查看:
q).z.b
t| v
End.