Function Specification
A function is a sequence of expressions to be evaluated, having optional input parameters and a return value.
Function Definition
The function simply returns the value of the (last) expression evaluated.
q){[x] x*x}[3]
9
q)f:{[x] x*x}
q)f[3]
9
Function Notation and Terminology
The formal notation for function definition is,
{[p1;...;pn] e1; ...; em}
The semi-colons in the body are separators, not terminators.
q)a:2*3;6*7
42
q)a
6
The number of formal input parameters (implicit or explicit) is the function valence. Most common are unary (valence 1) and binary (valence 2).
A nullary function in q is a function that has no meaningful input. This is similar to a function taking void in C. In functional programming this is represented as a function defined on a type called "unit" having a single value, often written (). In q it is written with an empty parameter declaration,
f:{[] … }
q){[] 42} / pure function returns constant 42
_
The maximum valence permitted as of this writing (Sep 2015) is 8; specifying more than eight parameters will cause an error. You can circumvent this restriction by encapsulating multiple parameters in a list or dictionary.
This convention can be supplanted by using a unary overload of : that immediately terminates function evaluation and returns the value of the expression to its right. This style is discouraged.
q){[x] :x*x} / unnecessary use of :
q){[x] a:1; :x*x; b:3} / dead code
Function Application
q){[x;y] x+y}[3;4]
7
q)g:{[x;y] x+y}
q)g[3;4]
7
Function application in q is strict, meaning that expressions in arguments are evaluated before substitution is performed.
q)f:{[x] x*x}
q)f[0N!3+1]
4
16
The valence of a function is also called its rank. An application with too many arguments generates a 'rank error.
q) {[x] x*x}[3;4]
'rank
Functions with No Return Value
For example, it may send off a message asynchronously without waiting for an acknowledgement of receipt or it may update a global table.
q)fvoid:{[x] `a set x;}
q)fvoid 42
q)`a
In fact, the actual return value is the nil item :: that is a stand-in for void in this situation. Its display is suppressed in the q console but it can be revealed.
q).Q.s1 fvoid 42
"::"
Application of a Name
q)f:{x*x}
q)f[5]
25
q).my.name.space.f:{2*x}
q)`.my.name.space.f[5]
10
q)g:{x+z}
q)g[1;2;3] / 2nd arg is required but ignored
4
Anonymous Functions and Lambda Expressions
A function without a name is then called an anonymous function.
q)powers:({1}; {x}; {x*x}; {x*x*x})
::
q)selected:2
::
q)powers[selected]
{x*x}
The Identity Function ::
When :: is used with function application syntax, it is the identity function – i.e., it returns its input as output.
The naked identity function cannot be used with prefix syntax; it must be enclosed in parentheses.
q)::[42]
42
q)::[`a`b`c]
`a`b`c
q):: 42 / error
`
q)(::) 42
42
Functions Are Data
q)(1; 98.6; {x*x})
1
98.6
{x*x}
q)f:{x*x}
q)(f; neg)[1; 5]
-5
Call-by-Name
q)a:42
q)get `a
42
q)`a set 43
`a
q)a
43
Local and Global Variables
Defining Local and Global Variables
A variable that is assigned with : within a function body is called a local variable.
q)f:{a:42; a+x}
As of this writing (Sep 2015), the maximum number of local variables permitted in a function is 24.
Some notes on local variables:
-
A local variable exists only for the duration of an application. There is no notion of a static local variable that maintains its value across applications.
-
A local variable is not visible outside its immediate scope of definition.
-
A local variable cannot be used as an argument to a q function that uses call-by-name.
-
A local variable is not visible within the body of a local function defined in the same scope. Otherwise put,
q does not have lexical scoping.
q)f:{[p1] a:42; helper:{[a; p2] a*p2}[a;]; helper p1} q)f 5 210
A variable assigned outside any function definition is called a global variable. Global variables are visible inside any function body.
q)b:7
q)f:{b*x}
q)f[6]
42
As of this writing (Sep 2015), the maximum number of global variables that can be referenced in a function is 32.
If this is a problem, redesign your code. For example collect values in a list or dictionary and pass that; the dictionary essentially provides pass-by-name.
Assigning Global Variables within a Function
This usage of :: is analogous to other forms of amend-in-place, such as +: or ,:, but using the assignment operator itself :. This accounts for its somewhat unintuitive properties – it is not truly a global assignment operator.
q)b:6
q)f:{b::7; x*b}
q)f[6]
42
q)b
7
If there is a local variable having the same name as the global you intend to assign, double-colon assigns the local, not the global.
q)b:6
q)f:{b:42; b::x; b}
q)f[98]
98
q)b
6
We recommend against :: for global assignment. Instead, use set which truly does global assignment and doesn’t get hung up on a local-global name collision.
As we saw earlier, set uses call-by-name.
q)a:42
q)f:{a:98.6; `a set x}
q)f 43
`a
q)a
43
Projection
Projection means specifying only some of the parameters in function application, resulting in a function of the remaining parameters.
Function Projection
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
Operator Projection
When used in infix form, a q operator can be projected by fixing its left operand (only). This requires parentheses.
q)(7*) 6
42
q)-[;42] 98
56
Multiple Projections
q){x+y+z}[1;;3]
{x+y+z}[1;;3]
q){x+y+z}[1;;3] 2
6
This is equivalent to projecting in the reverse order.
q){x+y+z}[;;3][1;]
{x+y+z}[;;3][1;]
q){x+y+z}[;;3][1;] 2
6
Indexing at Depth
q)d:`a`b!(1 2 3; 100 200)
q)d[`a;1]
2
Atomic Functions
Unary Atomic Functions and "map"
q)neg 10 20 30
-10 -20 -30
q)neg `a`b`c!(10 20; 30 40 50; 60)
a| -10 -20
b| -30 -40 -50
c| -60
Binary Atomic Functions and zip
For example, Find (?) is atomic in only its second argument since it consumes the entire first argument in its search.
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
Similar behavior is achieved with "zip" in traditional and functional programming.
Creating Atomic Functions
Unary
q)f:{(x*x)+(2*x)-1}
q)f til 10
-1 2 7 14 23 34 47 62 79 98
Binary
q)pyth:{sqrt (x*x)+y*y}
q)pyth[1 2 3; 1 2 3]
1.414214 2.828427 4.242641
Iterators
Iterators (previously adverbs) are higher-order functions that modify the behavior of functions for application on lists.
Unary each
q)neg (1 2 3; 4 5)
-1 -2 -3
-4 -5
q)count 10 20 30
3
q)count each (10 20 30; 40 50)
3 2
q)each[count] (10 20 30; 40 50)
3 2
The higher-order function each is similar to "foreach" in imperative languages and is called "map" in functional programming languages.
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) 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)neg each (1 2 3; 4 5)
-1 -2 -3
-4 -5
q)enlist each 1001 1002 1004 1003
1001
1002
1004
1003
q)flip enlist 1001 1002 1004 1003
1001
1002
1004
1003
Each '
An atomic operator automatically applies to corresponding pairs of items when applied to lists of the same length.
q)("abc"; "uv"),'("de"; "xyz")
"abcde"
"uvxyz"
There is no effect when the arguments are atoms.
q)3,'4
3 4
The modified function also extends an atom to match a list.
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
A reliable way to make a list of pairs from a pair of lists is:
q)flip (L1; L2)
,`a 1
`b 2
A nice example of ,' arises when both operands are tables. Since a table is a list of records, it is possible to apply ,' to tables with the same number of rows.
q)t1:([] c1:1 2 3)
q)t2:([] c2:`a`b`c)
q)t1,'t2
c1 c2
-----
1 a
2 b
3 c
作者:Jeffry A. Borror