共计 4357 个字符,预计需要花费 11 分钟才能阅读完成。
0. “Operators and Verbs Are Functions”
在 q 中,操作符 (Operators) 又称为动词(Verbs), 读表达式 3 + 2 按照从右到左的顺序:3 被加到 2,其中 3 是一个名词(主语),操作符 + 是一个动词,2 是一个名词(宾语)。
1. 函数标识
下面介绍三种以后常见的函数分类:monadic 函数:f[x] 或者 f xdyadic 函数:g[x;y] 或者 x g yatomic 函数:作用于数据结构的那个元素
例如 + 是一个 dyadic 函数,它有如下两种表达方法,都是等价的:
q) 2+3
q) +[2;3]
第二个式子很神奇,类似的表达方式还有:
q)=[2;3]
0b
更神奇的是,可以将二元运算符的前缀和中缀组合在一起:q)(2+)[3]5q)(2+)35
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 evaluated left-of-rightwhich equates toExpressions are evaluated right-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 和 余数 moddiv 的结果是向下取整,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 与 ceilingfloor 向下取整,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.