美文网首页
Q语言——函数

Q语言——函数

作者: Hero_cxs | 来源:发表于2019-04-21 23:33 被阅读0次

    前言

    前面介绍过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
    

    相关文章

      网友评论

          本文标题:Q语言——函数

          本文链接:https://www.haomeiwen.com/subject/aiosgqtx.html