QforMortals2/primitive operations

From Kx Wiki
Jump to: navigation, search

Contents

Primitive Operations

Introduction to Functions

Function Notation

Operators and functions are closely related. In fact, operators are just functions used with infix notation. We cover functions in depth in Functions, but provide a brief overview here. Function evaluation in q uses square brackets to enclose the arguments and semicolons to separate them. Thus the output value of a monadic function f for the input x is written,

       f[x]

Similarly, the value of a dyadic function is written,

        f[x;y]

The simplest functions are those whose domain and range are atomic data types. These functions are called (what else?) atomic functions.

Primitives, Verbs and Functional Notation

The normal way of writing addition in mathematics and most programming languages uses an operator with infix notation - that is, a plus symbol between the two operands,

        2+3

In q, we can also consider addition to be a dyadic function that takes two numeric arguments and returns a numeric result. You probably wouldn't think twice at seeing,

         sum[a;b]

But you might blink at the following perfectly logical equivalent,

        +[a;b]

A dyadic function that is written with infix notation is called a verb. This terminology arises from thinking of the left operand as the subject which acts on the right operand as object.

The primitive operators are the built-in atomic verbs, including the basic arithmetic, relation and comparison operators. Some are represented by a single ASCII symbol such as '+', '-', '=', and '<'. Others use compound symbols, such as '<=', '>=', and '<>'. Still others have names such as 'not', 'neg'. The extent of operations is not limited to the primitives, since any monadic or dyadic function can be made into a verb.

Any verb, including all the primitive operators, can also use regular function notation. So, in q you can write,

        +[2;3]
5

It is even possible, and sometimes useful, to write a binary verb using a combination of infix and functional notation for the two operands. This may look very strange at first,

        (2+)[3]
5

It is even possible to write,

	(2+)3
5

Item-wise Extension of Atomic Functions

A fundamental feature of an atomic function or operator is that its domain is extended to lists by item-wise application. Thus, a monadic atomic function is applied to a simple list by operating element-wise on the list. A dyadic atomic operator is extended to operate on an atom and a simple list by applying its operation to the atom and the items in each position of the list. Similarly, a dyadic atomic operator is extended to operate on a pair of simple lists by operating pair-wise on elements in corresponding positions.

Symbolically, let m be a unary atomic verb, op a binary atomic verb, a an atom, L, L1 and L2 simple lists, and i an int index. Then,

i th element of is
m[ L] m[ L[ i] ]
a op L a op L[ i]
L op a L[ i] op a
L1 op L2 L1[ i] op L2[ i]

For example, the result of applying neg to a simple list is obtained by application to each item of the list.

        L:100 200 300 400
        neg L
-100 -200 -300 -400

The result of adding an atom to a simple list is obtained by adding the atom to each item of the list.

        99+L
199 299 399 499

The result of adding two simple lists of the same length is addition of items at corresponding positions.

        L1:100 200 300 400
        L2:9 8 7 6
        L1+L2
109 208 307 406

Operator Precedence

Traditional Operator Precedence

Recall that mathematical notation and verbose programming languages have a concept of operator precedence, which attempts to resolve ambiguities in the evaluation of arithmetic and logical operations in expressions. The arithmetic precedence rules were drummed into you in elementary school: multiplication and division are equal and come before addition and subtraction, etc. There are similar precedence rules for =, <, >, 'and' and 'or'.

Left of Right Evaluation

Although the traditional notion of operator precedence has the weight of many years of incumbency (not to mention the imprecations of your fifth grade math teacher), it's time to throw the bum out. As mentioned in atoms, q has no rules for operator precedence. Instead, it has one simple rule for evaluating any expression:

Expressions are evaluated left of right

We could also say "right to left" since the interpreter evaluates an expression from right-to-left. However, every action in q is essentially a function evaluation, and it is more natural to read "f of x" rather than "x evaluated by f". Thinking functionally makes "of" a paradigm, not just a preposition.

The adoption of left-of-right expression evaluation frees q to treat infix notation simply and uniformly. Which notation is used, infix or functional, depends on what is clearer in the specific context.

Left-of-right expression evaluation also means that there is no ambiguity in any expression. (This is from the compiler's perspective; it is certainly possible to write q expressions comprehensible to only the compiler and q gods). Parentheses can still be used to override the default evaluation order but there will be far fewer once you abandon the old (bad) habit of using them to override operator precedence. You should arrange your expressions with a goal of placing parentheses on the endangered species list.

A Gotcha of Left of Right Evaluation

Due to left-of-right evaluation, parentheses are needed to isolate the result of an expression that is the left operand of a verb. Omitting such parentheses is a common error for q newbies, as this grouping is often unnecessary in verbose languages.

Here is a canonical example, where < and > have their usual meanings. As we shall see shortly, the | operator returns the maximum of its operands; this reduces to "or" for binary types. It is a rite of passage of q newbies to write the first expression intending the second,

        x:100
        x<42|x>98
0b
        (x<42)|x>98
1b

The first expression parses from right to left as:

  • x is tested against 98 by greater than, yielding 1b, which is compared for the larger to 42, yielding 42, against which x is tested by less than, yielding 0b.

The second expression parses from right to left as:

  • x is tested against 98 by greater than, yielding 1b, which is compared for the larger to 0b (being the result of testing x against 42 by less than), yielding 1b.

Should this seem unnatural, don't worry. Once you complete this chapter, revisit here and it'll feel right as rain.

Rationale for No Operator Precedence

Operator precedence is quite feeble in that it requires all the components of an expression to be analyzed (think for a moment about how you do it manually) before it can be evaluated. Ironically, it results in the frequent use of parentheses to override the very rules that are purportedly there to help.

Even more damning is that operator precedence forces semantic content onto infix notation. Suppose a programming language wished to allow dyadic functions to be verbs - i.e., expressed in infix notation - so that

        f[x;y]

can also be written,

        x f y

This would entail the extension of precedence rules to cover verbs whenever they are mixed with arithmetic operations. Aside from being impractical, this would result in yet more parentheses.

Match (~)

The non-atomic, binary match operator ( ~ ) applies to any two entities, returning a boolean result of 1b if they are identical and 0b otherwise. For two entities to match, they must have the same shape, the same type and the same value(s), but they may occupy separate storage locations. Colloquially, clones are considered identical in q because they are indistinguishable.

Information.png Advanced: This differs from the notion of identity in some verbose languages, in that distinct q entities can be identical. For example, in languages of C ancestry, objects are equal if and only if their underlying pointers address the same memory location. Identical twins are not equal. You must write your own equivalence method to determine if one object is a deep copy of another.

There are no restrictions as to the type or shape of the two operands for match. Try to predict each of the following results of match,

        42~42
1b
        42~42h
0b
        42f~42.0
1b
        42~`42
0b
        `42~"42"
0b
        4 2~2 4
0b
        42~(4 2;(1 0))
0b
        (4 2)~(4; 2*1)
1b
        (1 2; 3 4)~(1; 2 3 4)
0b

While you are learning q, applying match can be an effective way to determine if you have entered what you intended, or to discover whether two different ways of expressing something produce the same result. For example, q newbies often trip over

        42~(42)
1b

This technique can be useful in checking intermediate results when debugging (except for the q gods who enter perfect q code every time).

Relational Operators

The relational operators are atomic verbs that return boolean results. Relational operations on atomic types have requirements regarding the compatibility of the operands.

Equality (=) and Inequality (<>)

We begin with the equality operator ( = ), which differs from match in that it is atomic, so it tests its operands component-wise instead of in entirety. All atoms of numeric or char type are mutually compatible for equality, but symbols are compatible only with symbols.

Equality is not strict with regard to type, meaning types with the same underlying value are equal. For example, chars are equal to their underlying values.

 #!q
       42h=2*21
1b
        42=42.0
1b
        42=(42)
1b
        42=0x42
0b
        42="*"
1b

A symbol and a character are not compatible and an error results from the test,

        `a="a"
'type

The not-equal primitive is ( <> ).

        42<>0x42
1b
Warning.png Note: The test "not equal" can also be expressed by applying not to the result of testing with =.
        a:42
        b:98.6
        a<>b
1b
       not a=b
1b
Warning.png Note: When comparing floats, q uses multiplicative tolerance, which makes arithmetic give rational results.
        r:1%3
        r
0.3333333
        2=r+r+r+r+r+r
1b

Not Zero (not)

The monadic, atomic relational operator not differs from its equivalent in some verbose languages. It returns a boolean result and has a domain of all numeric and character types; it is not defined for symbols. The not operator generalizes the reversal of true and false values to any entity having an underlying numeric value by testing its argument against an underlying 0. In other words, it answers the Hamletonian question: to be, or not to be, zero.

The test against zero yields the expected results for boolean arguments.

        not 0b
1b
        not 1b
0b

More generally, the test against zero apples for any numeric type.

        not 42
0b
        not 0
1b
        not 0j
1b
        not 0xff
0b
        f:98.6
        not f
0b
        not 0.0
1b

For char values, not returns false except for the character representing the underlying value of 0.

        not "a"
0b
        not " "
0b
        not "\000"
1b

For date and datetime values, not tests against midnight of Jan 1, 2000, since this is the datetime with underlying value 0.

        not 2042.04.02
0b
        not 2000.01.01T00:00:00:000
1b
        not 2000.01
0b

The last example obtains because omitted temporal constituents default to their underlying numeric 0 values.

For time values, not tests against 00:00:00.000.

         not 00:00:00.000
1b
         not 04:02:42.042
0b

Ordering: <, <=, >, >=

We consider the binary atomic order operators. Less than ( < ), greater than ( > ) less or equal ( <= ) and greater or equal ( >= ) are defined for all atoms with the requirement that the operands be of compatible types. Numeric and char types are mutually compatible, but symbols are only compatible with symbols. Comparison for numeric and char types is based on underlying numeric value, independent of type.

        4<42
1b
        4h>=0x2a
0b
        -1.59e<=99j
1b

For char atoms, the underlying numeric value results in comparison according to ASCII character sequence.

        "A"<"Z"
1b
        "a"<="Z"
0b
        "A"<"0"
0b
        "?"<"/"
0b

A numeric atom and a char are compared according to the underlying numeric value of the char.

        42<"z"
1b

For symbols, comparison is based on lexicographic order.

 #!q
       `a>=`b
0b
        `ab<`abc
1b

Now that we are familiar with relational operations on atoms, let's examine their item-wise extensions to simple lists.

        2<1 2 3
001b

        1 2 3h>=-987.65 1.234 567.89
110b

        " "="Life the Universe and Everything"
00001000100000000100010000000000b

        "zaphod"="Arthur"
000100b

        "zaphod">"Arthur"
100000b
Warning.png Note: As of this writing (Jun 2007), the primitive > is converted to the equivalent < under the covers by the q interpreter. That is,
        a>b

is actually evaluated as,

        b<a

This does not matter when a and b are atoms or lists, but it does have consequences when they are dictionaries.

Basic Arithmetic: +, -, *, %

The arithmetic operators are atomic verbs and come in two flavors: binary (in the mathematical sense of having two operands) and unary (one operand). We begin with the four operations of elementary arithmetic.

Symbol Name Example
add 42+67
* times 2h*3h
% divide 42%6

On the surface, things look pretty much like other programming languages, except that division is represented by % since / is used to delimit comments. We have,

        6*7
42
        a:42
        b:3
        c:a-b
        c
39
        100*a
4200
        c%b
13f
Warning.png Note: The result of division is always a float.

For a programmer not accustomed to left-of-right evaluation, the following may take some getting used to.

        2*1+1
4

Things can get funky fast for the q newbie.

        c:1000*b:1+a:42
        c
43000

One way to read this is:

The integer value 42 is assigned to the variable named a, then the assigned value is added to 1, then this result is assigned to the variable named b, whose assigned value is multiplied by 1000 and the result is assigned to the variable named c

The arithmetic operations are defined for all numeric types, and all numeric types are compatible. The type of the result depends on the operands. Loosely speaking, smaller types are promoted to their wider cousins and division always results in floats. Typing does not get in the way of arithmetic.

When binary types participate in addition, subtraction and multiplication, they are promoted to int. In other words, arithmetic is not performed modulo 2 (i.e., in base 2) for binary values, or modulo 256 for byte values.

        1b+1b
2
        0x2a+0x11
59
        42+1b
43
        5*0x2a
210

When integer types are used in addition, subtraction and multiplication, the result is an int or the widest type present, whichever is wider.

        a:42
	b:123h
	c:1234567890j
	b+b
246
	a+b
165
	a+b+c
12345678055j

The result of addition, subtraction and multiplication of integer data types is modulo the width of the result. That is, overflow is ignored. For example, int arithmetic is modulo 2^32^.

        i:2147483647
        i+3
- 2147483646

When any numeric types participate in division, they are promoted to float and the result is a float.

        1%3
0.3333333
        3%1
3f

When floating point data types are mixed, the result is float.

        6.0*7.0e
42.0
Warning.png Note: The arithmetic operators are always dyadic. In particular, while ( - ) is also used syntactically to denote a negative number, there is no unary function ( - ) to negate a value. Its attempted use for such generates an error. Use the operator neg for this purpose.
        a:-4
        a
-4
        -a                / This is an error
'-
        neg a
4

According to the discussion in Match, the arithmetic operators are extended item-wise to lists. Thus,

        2+100 200 300
102 202 302

        b:1000.0 2000.0 3000.0 4000.0
        b*2
2000 4000 6000 8000f

        c:2 4 6 8
        b%c
500 500 500 500f

In the following example, observe that item-wise atomic application is recursive when all the list components are numeric,

        e:(100 200;1000 2000)
        e-2
98  198
998 1998

Max (|) and Min (&)

The comparison operators are atomic and binary, and return the type of the widest operand. Numeric types and char are mutually compatible; comparison is not defined for symbols.

The max operation ( | ) returns the maximum of its operands based on underlying numeric values; this reduces to logical "or" for binary operands. The min operation ( & ) returns the minimum of its operands based on underlying numeric values; this reduces to logical "and" for binary operands. The same type promotion rules apply as for the arithmetic operators.

         0b|1b
1b
        1b&0b
0b
        42|0x2b
43
        4.2e&42j
4.2e
        "a"|"z"
"z"
        "0"&"A"
"0"
        `a|`z                / this is an error
`type

Following are examples of comparison extended item-wise to simple lists.

        2|0 1 2 3 4
2 2 2 3 4
        11010101b&01100101b
01000101b
        "zaphod"|"arthur"
"zrthur"
Warning.png Note: For the symbolically challenged, the operator | can also be written as or. The operator & can be written as and.
        1 and 3
1
        "a" or "z"
"z"

Exponential Primitives: sqrt, exp, log, xexp, xlog

sqrt

The atomic unary sqrt has as domain all non-negative numeric values and returns a float representing the square root of its argument.

         sqrt 2
1.414214
         sqrt 4
2f
         sqrt 0x42
8.124038
         sqrt -1
0n

exp

The atomic unary exp has as domain all numeric values and returns a float representing the base e raised to the power of its argument.

        exp 1
2.718282
        exp 4.2
66.68633
        exp -12h
6.144212e-06
Warning.png Note: Do not confuse the 'e' used in the display of scientific notation with the mathematical base of natural logarithms.

log

The atomic unary log has as domain all numeric values and returns a float representing the natural logarithm of its argument.

        log 1
0f
        log 0x2a
3.73767
        log 0.0001
-9.21034
        log -1
0n

xexp

The atomic binary xexp has as domain all numeric values in both operands and returns a float representing the left operand raised to the power of the right operand. If the mathematical operation does not make sense, the result is 0n.

        2 xexp 5
32f
     -2 xexp .5
0n

xlog

The atomic binary xlog has as domain all numeric values in both operands and returns a float representing the logarithm of the right operand with respect to the base of the left operand. If the mathematical operation does not make sense, the result is 0n.

        2 xlog 32
5f
        2 xlog -1
0n

More Primitives: mod, signum, reciprocal, floor, ceiling and abs

These functions are useful in calculations.

Modulus (mod)

The binary mod is atomic in its left operand (dividend) which is any numeric value. The right operand (divisor) is a numeric atom. The result is the remainder of dividing the dividend by the divisor. This produces the usual remainder from elementary school for positive integers but is somewhat more complex for general numeric arguments.

For a positive divisor, the remainder is defined as the difference between the dividend and the largest integral multiple of the divisor not exceeding the absolute value of the dividend.

        4 mod 3
1
        0x2a mod 0x10
10
        4.5 mod 2.3
2.2
        4.5 mod -2.3
-0.1

Sign (signum)

The atomic unary signum has as domain all integral and floating point types and returns an int representing the sign of its argument. Here 1 represents "positive", -1 represents "negative" and 0 represents a zero argument.

        signum 4.2
1
        signum -42
-1
        signum 0
0

reciprocal

The atomic unary reciprocal has as domain all numeric types and returns a float representing 1.0 divided by the argument.

        reciprocal 0.02380952
42.00001
        reciprocal 0
0w

floor

The atomic unary floor has as domain int and floating point types and returns an int representing the largest integer that is less than or equal to its argument.

        floor 4
4
        floor 4.0
4
        floor 4.2
4
        floor -4.0
-4
        floor -4.2
-5

The floor operator can be used to truncate or round floating point values to a specific number of digits to the right of the decimal.

        a:4.242
       0.01*floor 100*a
4.24
        0.1*floor 0.5+10*a
4.2
Warning.png Note: The floor function does not apply to boolean, byte, or short types.
        floor 0x2a
'type

ceiling

Analogous to floor, the atomic unary ceiling has as domain int, long and floating point types and returns the smallest int that is greater than or equal to its argument.

        ceiling 4
4
        ceiling 4.0
4
        ceiling 4.2
5
        ceiling -4.0
-4
        ceiling -4.2
-4
Warning.png Note: For reasons known to the q gods, ceiling does apply to boolean or byte types but not to short type.
        ceiling 0b
0
        ceiling 42h
'type

Absolute Value (abs)

The atomic unary abs has as domain all integral and floating point types. It returns its argument if the argument is greater than or equal to zero, or neg applied to its argument otherwise. The result of abs has the same type as the argument.

        abs 4
4
        abs -4
4
        abs -4.2
4.2
        abs -4.0
4f
        abs -4.2e
4.2e
        abs -4j
4j

Operations on Temporal Values

We have separated temporal types and their operations into this section because they have richer semantics.

Internal Format of Temporal Types

First, we note that a date or datetime is actually stored under the covers as a signed float, with 0.0 corresponding to midnight of January 1, 2000. So,

        0.0=2000.01.01T00:00:00:000
1b

The integral part of the floating point value corresponds to the number of days after (positive) or before (negative) the start of the millennium. The decimal portion of a datetime is the fractional portion of a 24-hour day represented by its time component. Thus,

        33.5=2000.02.03T12:00:00.000
1b

Time is stored as the number of milliseconds from the start of day. Thus, a time value is between 0 and 86,400,000 (24*60*60*1000). So,

        43200000=12:00:00.000
1b

Basic Operations

In contrast to some verbose languages, any expression involving temporal types and numerical types that should make sense actually does, and it works in the expected fashion. Comparison of dates or datetimes reduces to comparison of the underlying floating point values. Thus,

        2006.01.01T00:00:00.000<2005.12.25T12:00:00.000
0b
        2005.12.25=2005.12.25T00:00:00.000
1b
        2005.12.25<2005.12.25T12:00:00.000
1b

Time values can be compared with each other and the result is based on the underlying millisecond counts.

        12:01:10.987<17:05:42.986
1b

A date and a time can be added to give a datetime.

        2007.07.04+12:45:59.876
2007.07.04T12:45:59.876
Warning.png Note: A time is implicitly converted to a fractional day when it is added to a date to get a datetime.

Day Counts and Time Counts

A date or datetime can be compared, or tested for equality, with a float,

        366.0=2001.01.01
1b

A time can be compared with an int.

        43200000<12:00:00.001
1b

A float representing a fractional day count can be added to or subtracted from a datetime (or date) to give a datetime. In this context, the integral part of the fractional day count represents the number of days and the decimal part represents the fractional part of a 24-hour day. For example, to move forward 33 days and 12 hours,

        2000.01.01T00:00:00:000+33.5
2000.02.03T12:00:00.000

Or, to move back 2 hours and 30 minutes,

        2000.01.01T00:00:00:000-2.5%24
1999.12.31T21:30:00.000

An int representing a day count can be added to or subtracted from a date to give a date.

        2006.07.04+5
2006.07.09

The difference of two datetimes is a float representing the fractional day count between them.

        2007.02.03T12:00:00.000-2007.01.01T00:00:00:000
33.5

The difference between two dates is an int day count representing the number of days between them.

        2006.07.04-2006.04.04
91

An int representing a time count of milliseconds can be added to or subtracted from a time to give a time.

        12:00:00.000+1000
12:00:01.000

The difference between two times is an int count of the number of milliseconds between them.

        23:59:59.999-00:00:00.000
86399999

Observe that a time does not wrap when it exceeds 24 hours.

        23:59:59.999+2
24:00:00.001

Operations on Infinities and Nulls

As you gain experience with the way q handles infinities and nulls, you'll find that it is simpler and more rational than verbose languages. Injection of such an exceptional value into a calculation stream propagates through subsequent steps in a predictable way without the need for special error trapping and handling. While the result will contain some meaningless data, portions that do not depend on the invalid values will still compute correctly.

Producing Infinities

We show how to produce and operate with the infinities we met in Infinities and NaN. Division of a non-negative numeric value by any 0 results in float infinity, denoted by 0w.

        4.0%0
0w
        3.14%0.0
0w
        0x32%0
0w
        1b%0
0w

Similarly, division of a negative numeric value by any 0 results in negative float infinity, denoted by -0w.

        -4%0.0
-0w
        -3.14%0
-0w

The int infinities can not be produced via an arithmetic operation on normal int values, since the result of division in q is always of type float.

        42%0
0w
        -42%0
-0w

Producing NaN

When any numeric zero is divided by zero, the mathematical result is undefined. This is sometimes represented in writing as NaN ("not-a-number"). It is denoted in q by 0n, which is the float null value,

        0%0
0n
        0.0%0.0
0n
        0.0e%0b
0n
        0j%0x00
0n

Basic Arithmetic on Infinities and Nulls

The infinities and nulls act reasonably in numeric expressions and comparisons. Generally, if one member of an expression is infinite or null so is the result. In an arithmetic mix of infinity, null or NaN, the null prevails over infinity and NaN prevails over other nulls. Note that the signs of infinities are carried correctly through arithmetic and meaningless expressions involving infinities result in NaN.

        2+0w-3
0w
        0w*-0w
-0w
        -0w+0w
0n
        42+0n
0n
        42+0N
0N
        0w+0n
0n
        0n+0N
0n

The exception to the above is that any integral infinity can be added to its negative infinity to yield 0.

        -0Wj+0Wj
0j

Type Promotion

When nulls occur in expressions of mixed type, the same type promotion rules apply as for finite values.

        42+0N
0N
        42j+0N
0Nj
        0N+0Nj
0Nj
        0n+0N
0n

Equality

Infinities are distinct from all numeric values and from all nulls as well, since they do not represent missing data. All nulls are equal since they differ only by type.

        42=0W                / can compare a numeric value to infinity
0b
        0w=42%0            / can compare float infinity to itself
1b
        0=0N                   / 0 is not the same as missing integer
0b
        0=0n                   / 0 is not the same as missing float
0b
        0w=0W               / float infinity is not the same as int infinity
0b
        0w=0N                / float infinity is not the same as null integer
0b
        0w=0n                / float infinity is not the same as missing float
0b
        0Nj=0N               / missing long and missing int are the same
1b
        0N=0n                / missing int and missing float are the same
1b
Warning.png Note: In contrast to some languages, such as C, separate NaNs are equal.
        (0%0)=0%0
1b
Information.png Advanced: The integral infinities, positive and negative, have underlying values whose bit patterns correspond to legitimate base-2 integral values.
Value Bit Representation
0Wh 0111111111111111b
0W 01111111111111111111111111111111b
0Wj 0111111111111111111111111111111111111111111111111111111111111111b

Consequently, we find

        32767=0Wh
1b
        2147483647j=0W
1b
        -32767=-0Wh
1b
        -2147483647j=-0W
1b

Match

Match is a different story because type matters.

        42~0w                    / can try to match a numeric value to infinity
0b
        0w~42%0                / can match infinity to itself
1b
        0~0N                       / 0 does not match an missing integer
0b
        0~0n                        / 0 does not match missing float
0b
        0w~0W                     / float infinity does not match int infinity
0b
        0w~0N                      / infinity does not match missing integer
0b
        0w~0n                      / infinity does not match missing float
0b
        0Nj~0N                     / missing long and missing int do not match
0b
        0N~0n                      / missing int and missing float do not match
0b

not

The not operator returns 0b for all infinities and nulls since they all fail the test of equality with 0.

        not 0w
0b
        not 0W
0b
        not 0N
0b
        not 0n
0b

neg

The neg operator returns -1 times its operand, so it reverses the sign on infinities but does nothing to nulls since sign is meaningless for missing data.

        neg 0W
-0W
        neg -0w
0w
        neg 0N
0N
        not " "
0b

Comparison

Comparisons apply to infinities and nulls, as summarized in the following diagram.

nulls < -0w < -0Wj < -0W < -0Wh < numeric values < 0Wh < 0W < 0Wj < 0w

As rules:

Warning.png Note: These relations characterize the infinities, in the sense that they are larger or smaller than all normal values. The integral infinities have underlying bit patterns corresponding to legitimate base 2 values that yield the above relations. Infinite arithmetic will parse, but the results are not particularly useful. It is recommended that you limit operations on integral infinities to equals, not equals and inequalities.

Some examples,

         42<0W
1b
        -0w<42.0
1b
        -0w<1901.01.01
1b
        -0w<0w
1b
        0W<0w
1b
        -0w<0W
1b
        -10000000<0N
0b
        0Nj<42
1b
        0n<-0w
1b

The null symbol is less than any other symbol

        `a<`                / the right side is the null symbol
0b

Max and Min

The behavior of | and & with infinities and nulls derives from that of equality and comparison.

        42|0W
0W
        -42&0N
0N
        0w|0n
0w
        -0w&0n
0n
        0n|0N
0n
        0n&0n
0n
        0W&0Wj
2147483647j

The last result obtains because int infinity is promoted to a long and its bit pattern corresponds to the listed value.

Alias (Advanced)

An alias is a variable that is defined as an expression involving other variables. This differs from ordinary assignment which defines a variable as the result of an expression.

Alias and Double assignment

Double assignment (::) outside a function defines the left operand as an alias of the right operand. When the alias is referenced, the underlying expression will be (re)evaluated. For example, the following defines b as an alias for a. Observe that changing the value of a is reflected in b but not in c:

	a:42
	b::a
	c:a
	b
42

	c
42

	a:98.6
	b
98.6

	c
42

Aliasing is useful when the underlying expression represents a calculation.

	u:4
	v:3
	w::v+sqrt u
	w
5f

	u:9
	w
6f

The result of aliasing can also be achieved with a function. In the previous example, we could define,

	f:{y+sqrt x}
	f[4;3]
5f

Aliasing provides convenient variable syntax instead of function semantics, but the dependencies are more evident in the function.

Alias chains are resolved and dependency loops are detected.

	a:42
	b::a
	c::b+1000
	b
42

	c
1042

	a:98.6
	b
98.6

	c
1098.6

	a::c
'loop
Information.png Advanced: Aliasing can be used to provide a view in a database by specifying a query as the right operand. For example
        t:([]c1:`a`b`c`a;c2:20 15 10 20;c3:99.5 99.45 99.42 99.4)
	va:select sym:c1,px:c3 from t where c1=`a
	va
sym px
--------
a   99.5
a   99.4

Dependencies

Double assignment establishes a dependency of the alias on the entities in its underlying expression. For example,

	u:4
	v:3
	w::u+v

establishes s dependency of w on u and v. Q maintains a list of dependencies in the dictionary .z.b.

	.z.b
u| w
v| w

Each entity in the domain of .z.b is mapped to the entities that depend on it. If we add an alias of u in our example, we find,

	.z.b
u| w z
v| w
Information.png Advanced: The table dependencies implicit in views are not reflected in .z.b.
	t:([]c1:`a`b`c`a;c2:20 15 10 20;c3:99.5 99.45 99.42 99.4)
	s:select c1,c3 from t where c2=20
	.z.b
u| w z
v| w

Prev: Lists Next: Functions

Table of Contents

©2006-2007 Kx Systems, Inc. and Continuux LLC. All rights reserved.

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox