接下来几章将进入 Q 语言的核心内容——函数、表、数据转换、查询和 I / O 等等,excited :)
1. 函数说明
在 q 中,函数是可以修改全局变量的,所以 q 并不是一个纯正的函数式语言。1. 函数定义使用花括号{和},函数的输入输出类型不用指定,甚至函数名都可以不用指定。如下是一个完整的函数定义:
q) {[x] x*x}
调用函数时,参数用中括号包围起来,参数通过分号; 分隔
q){[x] x*x}[3]
9
函数可以赋值给一个变量:
q)f:{[x] x*x}
q)f[3]
9
2. 函数标识和术语 函数的正式定义为{[p1;…;pn] e1; …; em},其中可选的 p1;…;pn 是正式的参数,e1; …; em 是执行的表达式,虽然是从左向右写的,但是仍然是从右向左执行的。
函数的参数个数被称为函数的 valence,最常见的函数是 monadic(valence 1)和 dyadic(valence 2)。
一个 niladic 函数是指一个没有输入的函数,表示如下:
f:{[] …}
例子如下:
q){[] 42} / pure function returns constant 42
42
q){[] a*a} / impure function: references global a
{[] a*a}
函数最大的参数个数 (valence) 为 8,超过 8 个参数将会报错。如果参数较多的话,将参数打包成 list 或者 dictionary 输入函数。
函数的输出值为函数最后一个表达式的结果:
q){[x] x*x}
q){[x;y] a:x*x; b:y*y; r:a+b; r}
Keep it short. 很多 q 语言中的函数都是紧凑并且模块化的,很多函数都是仅仅一行
3. 函数应用函数的调用是严格的,意味着在参数替换之前,表达式就已经被执行了:
q)f:{[x] x*x}
q)f[0N!3+1]
4
16
当提供的参数多于函数定义的参数时,会报 ’rank 错误。
q) {[x] x*x}[3;4]
‘rank
4. 没有返回值的函数函数体最后只有一个分号;,返回::。
q)fvoid:{[x] `a set x;}
q)fvoid 42
q)a
42
注意在 q 中,分号; 是分隔符而并不是终止符。
5. 参数的并列写法类似于列表索引和字典取值,函数的参数也可以是并列写法:
q){[x] 2*x} 42
84
q)f:{[x] x*x}
q)f 5
25
6. 函数名的应用当函数被赋值给一个全局变量时,可以通过 symbol 形式的函数名来调用:
q)f:{x*x}
q)f[5]
25
q)`f[5]
25
q)`f 5
25
q).my.name.space.f:{2*x}
q)`.my.name.space.f[5]
10
7. 隐含的参数当一个函数没有参数被明确定义时,三个隐含的参数 x, y, z 会被自动定义,下面的函数定义是等价的:
{[x] x*x}
{x*x}
{[x;y] x+y}
{x+y}
三个参数 x, y, z 的调用是按照先后顺序的,意味着 x 总是第一个被调用,y 第二个,z 第三个。下面这个函数只有提供三个参数值时才会返回值
q)g:{x+z} / likely meant x+y; requires 3 args in call
q)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 ignored
4
8. 匿名函数和 lambda 表达式一个没有函数名称的函数叫做匿名函数,匿名函数的常见的两个用处:
定义在函数内的匿名函数
f{[…] …; {…}[…]; …}
函数容器
q)powers:({1}; {x}; {x*x}; {x*x*x})
…
q)selected:2
q)powers[selected]
{x*x}
9. 恒等函数:: 恒等函数:: 返回它的输入作为输出;裸的恒等函数不能使用并列的形式,它必须使用中括号调用。
q)::[42]
42
q)::[`a`b`c]
`a`b`c
q):: 42 / error
‘
q)(::) 42
42
10. 函数是数据类似于 python 中的函数是对象一样,在 q 语言中函数都是数据,可以作为输入和输出。
q)apply:{x y}
q)sq:{x*x}
q)apply[sq; 5]
25
2. 通过名字调用
常规的函数使用传值调用(call-by-value),意味着参数在被传递时是按值传递的,在这个过程中,原始值被拷贝了一份,以保证之前的原始数据不会被修改。但这样做的一个问题是,当输入参数的 size 非常大时,拷贝是被禁止的。这个时候就有一个新的方法:call-by-name,在这种情况下变量的名字被传递而不是变量的值。
Call-by-name 没有特别的语法。一个例子就是内置函数 get,传递全局变量的名字,返回对应的值
q)a:42
q)get `a
42
另一个例子是函数 set,是给全局变量赋值
q)`a set 43
`a
q)a
43
3. 局部和全局变量
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 5
210
在所有函数定义以外的变量被称为全局变量。
2. 在函数内对全局变量赋值当函数体内没有同名的局部变量时,可以使用双冒号:: 来对全局变量进行赋值;`q)b:6q)f:{b::7; x*b}q)f[6]42q)b7` 而当函数体内有同名的局部变量时,双冒号符操作的是局部变量,并不是全局变量
q)b:6
q)f:{b:42; b::x; b}
q)f[98]
98
q)b
6
相比::,更推荐使用 set 来进行全局变量修改, 这样就不会有局部变量名冲突了。
q)a:42
q)f:{a:98.6; `a set x}
q)f 43
`a
q)a
43
4. 投影
投影是指只指定函数的一部分参数,其结果会是其余参数的函数。
1. 函数投影一个函数投影的例子为:
q)add:{x+y}
q)add[42;]
{x+y}[42;]
q)add[42;][3]
45
q)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]
36
q)f:{x+y}
q)g
{x-y}[42;]
q)g[6]
36
2. 运算符投影 当使用运算符中缀形式的时候,一个 q 的运算符可以通过固定其左运算元来进行投影,这时需要括号。
q)(7*) 6
42
由于任何操作符都是一个函数,当然可以使用其前缀形式进行投影:
q)-[;42] 98
56
上面两个公式中的空格符都不是必须的:
q)(7*)6
42
q)-[;42]98
56
3. 多维投影当函数有多个参数的时候(valence > 2),函数可以有多个投影,例如下例
q){x+y+z}[1;;3]
{x+y+z}[1;;3]
q){x+y+z}[1;;3] 2
6
5. Atomic 函数
一个 Atomic 函数是指该函数直接作用于一个 q 数据结构中的 atom 数据。
1. Monadic Atomic 函数和 map 回忆一下,monadic 函数是指只作用于一个参数的函数,如下是一个 monadic atomic 函数的例子,该函数作用于一个字典
q)neg 10
-10
q)neg 10 20 30
-10 -20 -30
q)neg (10 20 30; 40 50)
-10 -20 -30
-40 -50
q)neg `a`b`c!10 20 30
a| -10
b| -20
c| -30
q)neg `a`b`c!(10 20; 30 40 50; 60)
a| -10 -20
b| -30 -40 -50
c| -60
2. Dyadic atomic 函数和 zipDyadic 函数是指作用于两个参数的函数。若将 dyadic 函数的非 atomic 部分固定下来(可以看成一个函数的投影),那么 dyadic 函数就变为 monadic 函数。如下例中的 dyadic 操作符?。
q)10 20 30?10
0
q)10 20 30?10 20 30 40 50
0 1 2 3 3
q)(enlist 10)?10
0
q)10 20?10
0
q)10 20 30 40 50?10
0
在算术中,比较和关系运算符都是 atomic 的,在这种应用下会有四种情况:
atom 和 atom
atom 和 list
list 和 atom
list 和 list
在最后一种情况下,两个 list 元素的长度必须相等
q)1+10
11
q)1+10 20 30
11 21 31
q)1 2 3+10
11 12 13
q)1 2 3+10 20 30
11 22 33
这个功能相当于传统语言中的 zip
3. 构造 atomic 函数简单想一想就可以明白,由 atomic 函数构成的函数仍然是 atomic 的,因此构造一个 atomic 函数的方法就是去包含内置的 atomic 函数。
构造 Monadic atomic 函数:
q)f:{(x*x)+(2*x)-1}
q)f 0
-1
q)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.414214
q)pyth[1; 1 2 3]
1.414214 2.236068 3.162278
q)pyth[1 2 3; 1 2 3]
1.414214 2.828427 4.242641
6. 副词
副词是高阶的函数,用以改变函数在列表上的应用方式。这个术语来自于将 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 30
3
q)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 2
2 3
q)each[each[count]] ((1 2 3; 3 4); (100 200; 300 400 500))
3 2
2 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 1003
1001
1002
1004
1003
q)flip enlist 1001 1002 1004 1003
1001
1002
1004
1003
2. each-both’ 副词 each-both 符号 ’ 作用在一个 dyadic 函数上,使得函数能够成对地作用在对应的列表元素上,符号 ’ 读作 ”zip”。
q)(“abc”; “uv”),'(“de”; “xyz”)
“abcde”
“uvxyz”
q)1,’10 20 30
1 10
1 20
1 30
q)1 2 3,’10
1 10
2 10
3 10
q)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:`a`b`c)
q)t1,’t2
c1 c2
—–
1 a
2 b
3 c
3. 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 20
1 10 1 20
2 10 2 20
3 10 3 20
q)raze 1 2 3,/:\:10 20
1 10
1 20
2 10
2 20
3 10
3 20
上述操作还是较为复杂,我们可以通过内置函数 cross 来得到上述结果
q)1 2 3 cross 10 20
1 10
1 20
2 10
2 20
3 10
3 20
可以注意到,若我们组合 each-left 和 each-right 时,我们便可以得到上述结果的转置
q)raze 1 2 3,\:/:10 20
1 10
2 10
3 10
1 20
2 20
3 20
6. Over /Over 操作符 / 是一个提供递归机制的高阶函数。它最简单的形式是修改一个 dyadic 函数,使其在一个 list 上累积函数作用的结果
q)0 +/ 1 2 3 4 5 6 7 8 9 10
55
注意:在运算符和 / 之间不能存在空格,因为 / 可以被用作注释。
运算符左运算元为累积计算的初始值,右运算元为待累积计算的列表。
但我们也可以省略左运算元(即初始值),这个时候需要我们对表达式做一些变换,被作用函数和 Over 运算符 / 用括号包住,这时右边列表的第一个元素就是初始值。
q)(+/) 1 2 3 4 5 6 7 8 9 10
55
上面的括号是必需项,被修改的函数实际上是一个 monadic 函数。
一些有用的 over 形式:
q)(*/) 1 2 3 4 5 6 7 8 9 10 / product
3628800
q)(|/) 7 8 4 3 10 2 1 9 5 6 / maximum
10
q)(&/) 7 8 4 3 10 2 1 9 5 6 / minimum
1
使用,/ 可以高效地移除列表的顶层嵌套,其对应的内置函数为 raze。
q)(,/)((1 2 3; 4 5); (100 200; 300 400 500))
1 2 3
4 5
100 200
300 400 500
q)raze ((1 2 3; 4 5); (100 200; 300 400 500))
1 2 3
4 5
100 200
300 400 500
7. Iteration/ 的另一种用法是作为循环代码的等价形式。在这个版本下,左运算元表示了循环的次数,右运算元为初始值。例如,计算 Fibonacci 数列:
q)fib:{x, sum -2#x}
q)10 fib/ 1 1
1 1 2 3 5 8 13 21 34 55 89 144
q)fib/[10;1 1]
1 1 2 3 5 8 13 21 34 55 89 144
另一个版本的 / 控制了循环的进行直至收敛,或者检测到一个环的存在。下面以牛顿法为例介绍这种循环的使用,我们使用牛顿法来寻找函数 {-2+x*x} 的根:
q)f:{-2+x*x}
q)secant:{[f;x;e] (f[x+e]-f x-e)%2*e}
q){x-f[x]%secant[f; x; 1e-6]}/[1.5]
1.414214
q 语言会判断当前的输出值与之前的输出值之间的大小,如果两个值之间相差在一定的 tolerance 以内,则认为算法收敛并且迭代完成;否则继续执行循环任务。
此外,q 语言通过每次比较运算结果和初始值是否 match(~)来判断当前程序是否存在环(loop),如果存在环,程序则终止:
q)newtcycle:{[xn] xn-((xn*xn*xn)+(-2*xn)+2)%-2+3*xn*xn}
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 1597
8. Scan \Scan 操作符 \ 同样是一个高阶函数,其作用与操作符 / 一样,与其不同的是,Scan\ 会返回中间的计算结果。可以把 Scan\ 当做是 Over/ 的 ”running” 版本。
q)0+\1 2 3 4 5 6 7 8 9 10
1 3 6 10 15 21 28 36 45 55
q)(*\)1 2 3 4 5 6 7 8 9 10
q)(|\)7 8 4 3 10 2 1 9 5 6
7 8 8 8 10 10 10 10 10 10
q)(&\)7 8 4 3 10 2 1 9 5 6
7 7 4 3 3 2 1 1 1 1
q)100 f\1 2 3 4 5 6 7 8 9 10
q)(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 101
0 -1 2 1 -1
与其它副词一样,each-previous :’ 也有一个 monadic 函数的形式。但在这种形式下,列表中的第一个元素不会被当做初始的前项,相反,它直接返回该项。
q)(-‘:)100 99 101 102 101
100 -1 2 1 -1
q)deltas 100 99 101 102 101
100 -1 2 1 -1
q)(%’:)100 99 101 102 101
100 0.98999999999999999 1.0202020202020201 1.0099009900990099 0.99019607843137258
q)ratios 100 99 101 102 101
100 0.98999999999999999 1.0202020202020201 1.0099009900990099 0.99019607843137258
保留第一项的动机是,可以通过保留的第一项来恢复整个 list
q)sums deltas 100 99 101 102 101
100 99 101 102 101
q)deltas sums 100 99 101 102 101
100 99 101 102 101
当我们需要返回的结果中都是变化值时,可以通过如下方法得到
q)deltas0:{first[x] -‘: x}
q)deltas0 100 99 101 102 101
0 -1 2 1 -1
一个使用 each-previous 的非常有用的工具是使用~ 判断连续项是否是 match 的。实际中,我们经常会关注两个连续项不同的情况,这种情况下使用内置函数 differ
q)(~’:) 1 1 1 2 2 3 4 5 5 5 6 6
011010001101b
q)not (~’:) 1 1 1 2 2 3 4 5 5 5 6 6
100101110010b
q)differ 1 1 1 2 2 3 4 5 5 5 6 6
100101110010b
可以对 differ 的结果使用 where 和 cut 来分隔列表
q)L:1 1 1 2 2 3 4 5 5 5 6 6
q)where differ L
0 3 5 6 7 10
q)(where differ L) cut L
1 1 1
2 2
,3
,4
5 5 5
6 6
下面我们来做一些 q 的练习
q)runs:(where differ L) cut L / store runs
q)ct:count each runs / store count of each run
q)runs where ct=max ct / find the runs of maximum length
1 1 1
5 5 5
用一行代码来实现上面的代码
q) runs where ct=max ct:count each runs:(where differ L) cut L
同样,我们可以上述技术来找到上升和下降子序列
q)L:9 8 7 11 10 12 13
q)(where -0W>’:L) cut L
9 8 7
11 10
,12
,13
q)(where 0W<‘:L) cut L
,9
,8
7 11
10 12 13
8. 一般应用
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@1
20
q)L:10 20 30 40
q)L@1
20
q)@[L; 1]
20
q)count@L
4
q)@[count; L]
4
q){x*x}@L
100 300 900 1600
q)d:`a`b`c!10 20 30
q)d@`a
10
q)@[d;`b]
20
当 @与 niladic 函数应用时,可以使用空元素:: 来代表空值
q)f:{6*7}
q)f[]
42
q)@[f; ::]
42
q)f@(::)
42
q)f@43
42
2. 动词.q 语言中,多元映射包括了:深度索引一个列表,从一个字典中取一个被嵌套的值和执行一个带有多个参数的函数等。高阶函数. 是 q 语言中多元应用的真正形式。它将多元映射投影到多个参数上,并且可以被写为中缀和前缀形式。
. 的右侧必须是一个 list
q)L:(10 20 30; 40 50)
q)L[1][0]
40
q)L[1; 0]
40
q)L . 1 0
40
q)d:`a`b`c!(10 20 30; 40 50; enlist 60)
q)d[`b][0]
40
q)d[`b; 0]
40
q)d . (`b; 0)
40
q)g:{x+y}
q)g[1; 2]
3
q)g . 1 2
3
可以配合 monadic 函数使用.,其效果如下
q)f:{x*x}
q)f@5
25
q)f . enlist 5
25
q)f . enlist 1 2 3
1 4 9
为了表示一个隐藏的索引,可以使用:: 来代替
q)m:(1 2 3;4 5 6)
q)m[0;]
1 2 3
q)m . (0; ::)
1 2 3
q)m . (::; 1)
2 5
对于一个 niladic 函数的. 执行形式,需要使用:: 生成一个 list。
q)f:{6*7}
q)f . enlist (::)
42
q)f . enlist 42
42
All data structures in q are composed from lists and dictionaries.
一些很好的练习:
q)L:10 20 30
q)L . enlist 1
_
q)m:(10 20 30; 100 200 300)
q)m . 0 1
_
q)ds:(`a`b`c!10 20 30; `x`y!100 200)
q)ds . (0; `b)
_
q)mix:(10 20 30; `a`b`c!(1; 2; (300 400)))
q)mix . (1; `c; 1)
_
q)dc:`c1`c2!(1 2 3; `a`b`c)
q)dc . (`c2; 1)
_
q)t:([]c1:1 2 3;c2:`a`b`c)
q)t . (1; `c2)
_
答案分别是 20 20 20 400 `b `b
3. 应用 Monadic 函数的 @
回忆 @的一般操作:
q)L:10 20 30 40 50
q)@[L;1]
20
q)@[L;0 1]
10 20
现在除了取值外,我们同时应用一个函数:
q)@[L;1;neg]
10 -20 30 40 50
q)@[L;0 2;neg]
-10 20 -30 40 50
注意到上述结果与正常在列表子集上的运算不同,正常只会返回在子集上运算的结果
q)neg L@0 1
-10 -20
而这个提升的版本会返回修改后的整个列表。
Monadic 函数使用 @的一般应用 的语法是
@[L;I;f]
其中 L 是列表,I 为索引的容器。这个形式可以泛化到任何可以被视为映射的数据结构,例如给定一个字典和一个键值列表
q)d:`a`b`c!10 20 30
q)ks:`a`c
q)@[d; ks; neg]
a| -10
b| 20
c| -30
上述操作都是在输入数据结构的拷贝上完成的。我们也可以通过 pass-by-name 的方法来进行 in-place 修改
q)L:10 20 30 40
q)@[L; 0; neg]
-10 20 30 40
q)L
10 20 30 40
q)@[`L; 0 ; neg]
`L
q)L
-10 20 30 40
4. 应用 Dyadic 函数的 @当 Dyadic 函数使用 @时,需要提供一个额外的运算元,显然运算元要与子集的大小匹配。除了一种额外的情况,当运算元是 atom 时,会被自动拓展到与子集相同大小。
q)L:10 20 30 40
q)@[L; 0 1; +; 100 200]
110 220 30 40
q)@[L; 0 1; +; 100]
110 120 30 40
q)d:`a`b`c!10 20 30
q)@[d; `a`b; +; 100 200]
a| 110
b| 220
c| 30
q)@[d; `a`b; +; 100]
a| 110
b| 120
c| 30
Dyadic 函数使用 @的一般应用 的语法是
@[L;I;g;v]
其中 L 和 I 与上小节定义一样,可以是任意能够被视为映射的数据结构;g 是一个 Dyadic 函数;v 是一个 atom 或者与 I 匹配的列表。
列表赋值: 一个非常有用的 Dyadic 函数应用是使用赋值符: 在子集上赋值
q)L:10 20 30 40
q)@[L; 0 2; :; 42 43]
42 20 43 40
与 Monadic 函数一样,in-place 操作可以通过 pass-by-name 形式
q)L:10 20 30 40
q)@[`L; 0 2; :; 42 43]
`L
q)L
42 20 43 40
5. 应用 Monadic 函数的. 总结一下,@是作用在数据结构的顶层,而. 则是深度索引。
重新回顾一下. 的前缀用法
q)m:(10 20 30; 100 200 300)
q).[m; 0 1]
20
q)d:`a`b`c!(10 20 30; 40 50; enlist 60)
q).[d; (`a; 1)]
20
应用 monadic 函数的. 形式:
q).[m; 0 1; neg]
10 -20 30
100 200 300
q).[d; (`a; 1); neg]
a| 10 -20 30
b| 40 50
c| ,60
同样,若想 in-place 修改,则使用 pass-by-name 形式。
可以使用:: 来代替隐藏的索引
q).[m; (0; ::); neg]
-10 -20 -30
100 200 300
q)d:`a`b`c!(100 200 300; 400 500; enlist 600)
q).[d; (`a; ::); neg]
a| -100 -200 -300
b| 400 500
c| ,600
q).[d; (::; 0); neg]
a| -100 200 300
b| -400 500
c| ,-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 30
100 200 300
q).[m; (::; 1); +; 1 2]
10 21 30
100 202 300
q)m
10 20 30
100 200 300
q).[`m; (::; 1); +; 1]
`m
q)m
10 21 30
100 200 300
q).[`m; (::; 1); :; 42]
`m
q)m
10 42 30
100 42 300
q)d:`a`b`c!(100 200 300; 400 500; enlist 600)
q).[d; (`a; 1); +; 1]
q).[d; (`a; ::); +; 1]
q).[d; (::; 0); +; 1]
q).[`d; (::; 0); :; 42]