Q语言——函数

2019-04-21  本文已影响0人  Hero_cxs

前言

前面介绍过Q语言有很多内置函数,非常的方便。作为一门语言,当然也支持自定义函数,Q语言的自定义函数可能与其他编程语言的自定义函数有一些不同。因为此文章着重分享一下Q预言的自定义函数。

一、函数

1. 定义

Function_name :{[P1;…;Pn] E1;…;Em }

P1到Pn表示函数的参数,E1到Em表示函数的表达式(语句)。目前最多支持一次传递8个参数给一个函数,超过8个以上的参数会导致错误。因此可以通过用列表或字典来封装多个参数来规避此限制。函数的内部的表达式(语句)执行顺序为从左到右(从E1到Em),但是每个表达式的执行顺序是从右到左。函数最终的返回值为Em表达式的结果。在定义函数时为指定参数则为无参函数。

q)f:{[x] x*x} /定义一个函数
q)f[2] /给函数传递参数值
4
q)f 2 /第二种参数传递方式,该方式的好处是可以同时传递多个值,且返回对应的计算值
4
q)f 2 3 4 5
4 9 16 25
q)f [2 3 4 5]
4 9 16 25
q)f:{[x] a:x*x; b:a} 
q)f[3]
9
q)f:{[x;y] a:x*x; b:x*y; c:y*y; d:a+b+c}
q)f[2;3]
19
q)f:{[x;y] a:x*x; b:x*y; c:y*y; d:a+b+c; return:d} /函数的返回值,不能直接像其他编程语言一样直接使用return,没有return关键字
q)f[2;3]
19
q)f:{[x;y] a:x*x; b:x*y; c:y*y; d:a+b+c; d} /函数的返回值,可以直接将需要返回的值的变量放在最后
q)f[2;3]
19
q)f[2;3;4] /传递的参数多余定义的参数时会报rank错误
'rank
q)const99:{[] 99} /定义一个常量函数
q)const99[2]
99
q)const99[4.99]
99
q){[x;y] x+y}[3;4] /函数的直接使用,也称为匿名函数
7
q)f:{[x] x*x}
q)f[0N!3+1] /对参数的一个提前处理,且会返回参数的处理结果
4
16
q)f[0N!3]
3
9
q)f[0N!3-10]
-7
49

2. 无返回值函数

很多时候,我们不需要有些函数有返回值,因此这时候就要自定义无返回值函数,只需要在最后一个表达式后面加上;号就可以。

q)f:{[x;y] a:x*x; b:x*y; c:y*y; d:a+b+c;} /定义无返回值函数
q)f[2;3] /传递参数后没有返回值
q)

3. 函数的调用

将函数赋值给全局变量时,可以按名称调用它,即使用变量的符号名代替变量本身。

q)f:{[x] x*x}
q)f[5]
25
q)`f[5] /使用`符号调用函数
25
q)`f 5
25

4. 匿名函数

匿名函数顾名思义就是不给函数名称和参数,但是参数默认只能是x,y,z。我们可以将多个函数定义在一个大的函数中。

f{[...] ...; {...}[...]; ...}

匿名函数的定义方式:

q)anonymous:({1};{x*x};{x*y};{x+y+z}) /这里只能是x,y,z。不能是其他变量,且传递参数时x,y,z一定要一一对应
q)anonymous[0]
{1}
q)anonymous[1]
{x*x}
q)anonymous[3]
{x+y+z}
q)anonymous[3][1;2;3]
6
q)anonymous:({1};{x*x};{x*z};{x+y+z})
q)anonymous[2][1;2;3]
3
q)anonymous[3][1;2;3]
6

5. 函数是一种数据

到目前为止我们遇到的Q数据实体是原子(各种类型),列表和字典。对于那些刚接触函数编程的人来说,函数也是数据可能会让人感到惊讶。函数可以像long或float一样传递。

q)(1; 98.6; {x*x}) /函数与其他数据类型组成列表
1
98.6
{x*x}
q)f:{x*x}
q)(f; neg) /不同的函数组成列表
{x*x}
-:
q)(f; neg)[0] /选择列表中的第几个函数
{x*x}
q)(f; neg)[1;5] /选择列表中第1个函数,并传递一个5的值
-5
q)(f; neg)[0;5] /选择列表中第0个函数,并传递一个5的值
25

二、局部变量与全局变量

1. 函数的局部变量与全局变量的区别

在函数体内的变量我们称为局部变量,在函数体外的变量我们成为全局变量,这里的局部变量和全局变量有一点点区别。

1)局部变量仅在函数的持续时间内存在。

2)局部变量在其直接定义范围之外是不可见的。

3)局部变量不能用作使用名称调用的形式作为参数。

4)局部变量在同一范围内定义的本地函数体内不可见(可以细细体会一下下面的例子)。

q)f:{a:42; a+x} /创建一个包含a的局部变量函数
q)f[2]
44
q)f:{[p1] a:42; helper:{[p2] a*p2}; helper p1} /这里的a应该属于局部变量还是全局变量呢?这里的a是一个局部变量。
q)f[2] /由于a是一个局部变量,因此helper函数没有权限使用局部变量a,这里就报错了。
'a
q)a:43 /定义一个全局变量a
q)f[2] /这时helper函数就可以使用全局变量a了并取a的全局变量值。
86
q)f1:{[p1] b:7; b*p1} /定义一个局部变量b
q)f1[2] /仔细对比一下与上一个f函数有啥区别
14

2. 如何修改局部变量与全局变量

q)b:8 /定义一个全局变量b
q)f:{[p1] b::9; b*p1} /可以使用::来在函数内修改全局变量
q)f[2] /计算时使用的修改后的全局变量值
18
q)b /全局变量已经被修改
9
q)b:10 /定义一个全局变量b
q)f:{[p1] b:11; b::p1; b} /当函数内部有相同的局部变量时,用::号修改全局变量不会成功
q)f[98]
98
q)b /全局变量b的值没有被修改
10
q)a:42 /定义一个全局变量a
q)f:{[p1] a:99; `a set p1} /可以使用`a(call by name)的形式来修改全局变量
q)f[2] /全局变量a的值修改成功
`a
q)a
2

三、投影

1. 什么是投影

对于多参数的函数,我们可以先指定其中部分参数的值,其余参数的值可以通过调用时再传递相应的参数值。

q)add:{[p1;p2] p1+p2} /定义一个add函数
q)add[2;] /仅给add函数传递p1参数值
{[p1;p2] p1+p2}[2;]
q)g:add[2;] /仅给add函数传递p1参数值,然后赋值给g
q)g[3] /这时,对于add两个参数的函数我们只需要传递一个参数就可以
5
q)f:add[;4]
q)f[3]
7

2. 多元投影

q)add:{[p1;p2;p3] result:p1-p2+p3} /定义一个add函数
q)p:add[1; ;3] /只传递p1和p3的参数值,并赋值给p变量
q)p[4] 
-6
q)p:add[;2;3] / 只传递p2和p3的参数值,并赋值给p变量
q)p[4]
-1
q)p:add[1;2;]   /只传递p1和p2的参数值,并赋值给p变量
q)p[4]
-5
q)p:add[1;;] /只传递p1的参数值
q)p[2;3]
-4

四、映射

1. 什么是映射

前面我们介绍了列表,字典。列表和字典是一种映射,其实,函数也是一种映射。列表的索引,字典的查找,函数的应用的表示方法非常的相似。

q)L:10 20 30 40 50 60
q)I:2 5
q)L[I]
30 60
q)d:`a`b`c!10 20 30
q)k:`a`c
q)d[k]
10 30
q)f:{[x] x*x}
q)f[2 5]
4 25
映射关系图

五、原子函数

1. 原子函数定义

Q语言作为矢量语言的表征源于大多数内置操作都是基于原子的。原子函数的特征在于它会递归到参数的结构,直到递归到最底层原子。具体参考下面实例。

2. 原子函数的应用

通过下面的实例我们发现这些函数的操作都会递归到列表,字典的最底层原子上。

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
q)10 20 30?10
0
q)10 20 30?10 20 30 40 50
0 1 2 3 3
q)10 20 30 10 20 30?10 20 30 40 50
0 1 2 6 6
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

3. 自定义原子函数

q)f:{(x*x)+(2*x)-1} /定义一个原子函数
q)f 0
-1
q)f til 10 /给原子函数f传递0~9的参数值
-1 2 7 14 23 34 47 62 79 98

六、操作副词

1. each

each将使全部的操作动词的作用范围下降一层,可以通过下面的实例领会。

q)count (10 20 30; 40 50)
2
q)count each (10 20 30; 40 50)
3 2
q)count each ((1 2 3; 3 4); (100 200; 300 400 500))
2 2 
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)count each (1 2 3; 10 20 30)
3 3
q)(count each) each (1 2 3; 10 20 30)
1 1 1
1 1 1
q)reverse "live"
"evil"
q)reverse ("life"; "the"; "universe"; "and"; "everything")
"everything"
"and"
"universe"
"the"
"life"
q)reverse each ("life"; "the"; "universe"; "and"; "everything")
"efil"
"eht"
"esrevinu"
"dna"
"gnihtyreve"
q)(reverse each) each ("life"; "the"; "universe"; "and"; "everything")
"life"
"the"
"universe"
"and"
"everything"
q)reverse each reverse ("life"; "the"; "universe"; "and"; "everything")
"gnihtyreve"
"dna"
"esrevinu"
"eht"
"efil"

2. each-both(’)

’ 符号是将符号左右两个数据结构合并,参考下面实例。

q)"abc","de" /前面介绍的,号合并,只能是相同的数据类型
"abcde"
q)"abc",'"de" /通过,’合并可以不同数据结构类型,但是长度必须一样
'length /返回长度不相等错误
q)"abc",'"def" 
"ad"
"be"
"cf"
q)t1:([] c1:1 2 3)
q)t2:([] c2:`a`b`c)
q)t1,t2 /不同的数据类型用,号合并直接出现类型不匹配
'mismatch /返回类型不匹配错误
q)t1,'t2 /通过,’合并不同数据结构类型
c1 c2
-----
1 a
2 b
3 c
q)("abc"; "uv"),'("de"; "xyz")
"abcde"
"uvxyz"
q)3,'4
3 4
q)("abc"; "uv"),'("de"; "xyz"; "uhoh")
'length
q)1,'10 20 30
1 10
1 20
1 30
q)2#'("abcde"; "fgh"; "ijklm")
"ab"
"fg"
"ij"
q)L1:(enlist `a; `b)
q)L2:1 2
q)L1,'L2
`a 1
`b 2

3. each-left(:)

\:操作符扩展右操作数中的原子以连接左侧操作数据结构。

q)("abc"; "de"; enlist "f") ,\: ">"
"abc>"
"de>"
"f>"
q)("abc"; "de"; enlist "f") ,\: (">";"a";"b")
"abc>ab"
"de>ab"
"f>ab"

4. each-right(/:)

/:操作符扩展左操作数中的原子以连接右侧操作数据结构。

q)"</" ,/: ("abc"; "de"; enlist "f")
"</abc"
"</de"
"</f"

5. cross product

两个列表的交叉连接将左侧的每个项目与右侧的每个项目配对。

q)1 2 3,/:\:10 20
1 10 1 20
2 10 2 20
3 10 3 20
q)("aa";"bb";"cc"),/:\:("dd";"ee";"ff")
"aadd" "aaee" "aaff"
"bbdd" "bbee" "bbff"
"ccdd" "ccee" "ccff"

6. 累计(/)

/是一个高阶函数,其提供在Q语言的递归原理机制。在其最简单的形式中,它修改列表上累积结果。

q)1 +/ 1 2 3 4 5 6 7 8 9 10 /累加
56
q)1 -/ 1 2 3 4 5 6 7 8 9 10 /累减
-54
q)1 */ 1 2 3 4 5 6 7 8 9 10 /累乘
3628800
q)1 %/ 1 2 3 4 5 6 7 8 9 10 /累除
2.755732e-007
q)1 |/ 1 2 3 4 5 6 7 8 9 10 /找最大值
10
q)1 &/ 1 2 3 4 5 6 7 8 9 10 /找最小值
1
q)f:{[x;y] 2*x+y}
q)0 f/ 1 /这里的相当于给函数f传递x=0,y=1
2
q)0 f/ 1 2 /这里的相当于给函数f传递x=0,y=1,通过函数f计算得到的结果传给x,然后y=2,再次计算
8
q)0 f/ 1 2 3 4 5 6 7 8 9 10 
4072

7. 迭代(:)

很多人都知道斐波那契数列,斐波那契数列数列通过编程语言来写就是递归的形式。通过Q语言可以通过:迭代的形式来写。

q)fib:{x,sum -2#x} /-2表示取后两个数
q)10 fib/ 1 1
1 1 2 3 5 8 13 21 34 55 89 144

8. sacn(\)

\是一个高阶函数,其行为就像/(累计)除了最后的计算结果外中间的计算结果也会返回。

q)1+\1 2 3 4 5 6 7 8 9 10
2 4 7 11 16 22 29 37 46 56
q)1 *\ 1 2 3 4 5 6 7 8 9 10
1 2 6 24 120 720 5040 40320 362880 3628800
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)f:{[x;y] 2*x+y}
q)1 f\ 1 2 3 4 5 6 7 8 9 10
4 12 30 68 146 304 622 1260 2538 5096

9. each-previous(’:)

可用通过’:操作符来操作当前操作数与前一个操作数之间各项计算

q)100 -': 100 99 101 102 101 /计算当前操作数与前一个操作数之间的差
0 -1 2 1 -1
q)100 +': 100 99 101 102 101 /计算当前操作数与前一个操作数之间的和
200 199 200 203 203
q)100 *': 100 99 101 102 101 /计算当前操作数与前一个操作数之间的积
10000 9900 9999 10302 10302
q)100 %': 100 99 101 102 101 /计算当前操作数与前一个操作数之间的伤
1 0.99 1.020202 1.009901 0.9901961
q)100 &': 100 99 101 102 101 /取当前操作数与前一个操作数之间小的值
100 99 99 101 101
q)100 |': 100 99 101 102 101 /取当前操作数与前一个操作数之间大的值
100 100 101 102 102
q)100 >': 100 99 101 102 101  /比较前操作数与前一个操作数之间大小
00110b
q)100 =': 100 99 101 102 101 /比较前操作数与前一个操作数之间大小
10000b

七、函数应用

前面介绍过列表、字典的索引方法以及函数的应用。这里我们也可以通过@和.符号来进行操作。

@[L;I;f;v] 
.[L;I;f;v]

L表示列表或字典或其他数据结构,I表示索引,f表示函数,V表示传递的参数

q)L:10 20 30 40 50
q)@[L; 1]
20
q)@[L; 0 2]
10 30
q)@[L; 1; neg]
10 -20 30 40 50
q)@[L; 0 2; neg]
-10 20 -30 40 50
q)d:`a`b`c!(10 20 30; 40 50; enlist 60)
q)d:`a`b`c!10 20 30
q)@[d; `a`b; +; 100 200]
a| 110
b| 220
c| 30
q)m:(10 20 30; 100 200 300)
q).[m; 0 1]
20
q).[m; 0 1; neg]
10 -20 30
100 200 300
q)d:`a`b`c!(10 20 30; 40 50; enlist 60)
q).[d; (`a; 1)]
20
q).[d; (`a; 1); neg]
a| 10 -20 30
b| 40 50
c| ,60
q).[d; (`a; 1); +; 20]
a| 10 40 30
b| 40 50
c| ,60
上一篇下一篇

猜你喜欢

热点阅读