1.9 Lists 101
The fundamental q data structure is a list, which is an ordered collection of items sequenced from left to right. The notation for a general list encloses items with ‘(’ and ‘)’ and uses ‘;’ as separator. Spaces after the semi-colons are optional but can improve readability.
q)(1; 1.2; `one) _
A few observations on lists.
- A list can contain items of different type; this usually requires wrapping the values in a variant type in other languages. That being said, it is best to avoid mixed types in lists, as their processing is slower than homogenous lists of atoms.
- Assuming you entered the above snippet, you have noticed that the console echoes the items of a general list one item per line.
- In contrast to most functional languages, lists need not be built up by “cons”-ing one item at a time, although they can be. Nor are they stored as singly-linked lists under the covers.
- In the case of a homogenous list of atoms, called a simple list, q adopts a simplified format for both storage and display. The parentheses and semicolons are dropped. For example, a list of underlying numeric type separates its items with a space.
q)(1; 2; 3) 1 2 3 q)(1.2; 2.2; 3.3) _ q)(2000.01.01; 2000.01.02; 2000.01.03) _
A simple list of booleans is juxtaposed with no spaces and has a trailing `b’ type indicator.
q)(1b; 0b; 1b) 101b
A simple list of symbols is displayed with no separating spaces.
(`one; `two; `three) `one`two`three
Homogenous lists of atoms can be entered in either general or simplified form. Regardless of how they are created, q recognizes a list of homogenous atoms dynamically and coverts it to a simple list.
Next we explore some basic operations to construct and manipulate lists. The most fundamental is (til), which takes a non-negative integer n and returns the first n integers starting at 0 (n itself is not included in the result).
q)til 10 0 1 2 3 4 5 6 7 8 9
We obtain the first 10 integers starting at 1 by adding 1 to the previous result. Be mindful that q always evaluates expressions from right to left and that operations work on vectors whenever possible.
q)1+til 10 1 2 3 4 5 6 7 8 9 10
Similarly, we obtain the first 10 even numbers and the first ten odd numbers.
q)2*til 10 _ q)1+2*til 10 _
Finally, we obtain the first 10 even numbers starting at 42.
q)42+2*til 10 _
Another frequently used list primitive is join (,) that returns the list obtained by concatenating its right operand to its left operand.
q)1 2 3,4 5 1 2 3 4 5 q)1 2 3,100 _ q)0,1 2 3 _
To extract items from the front or back of a list, use the take operator (#). Positive argument means take from the front, negative from the back.
q)2#til 10 0 1 q)-2#til 10 _
In particular, the idiom 0# returns an empty list of the same type as the first item in its argument. Using an atom argument is a succinct way to create a typed empty list of the type of the atom.
q)0#1 2 3 `long$() q)0#0 `long$() q)0# `symbol$()
Should you extract more items than there are in the list, (#) restarts at the beginning and continues extracting. It does this until the specified number of items is reached.
q)5#1 2 3 1 2 3 1 2
In particular, if you apply (#) to an atom, it will continue drawing that single atom until it has the specified number of copies. This is a succinct idiom to replicate an atom to a list of specified length.
q)5#42 42 42 42 42 42
As with atoms, a list can be assigned to a variable.
q)L:10 20 30
The items of a list can be accessed via indexing, which uses square brackets and is relative to 0.
q)L 10 q)L _ q)L _
Reprinted with the author's permission from: q for Mortals Version 3, An Introduction to Q Programming by Jeffry A. Borror.
©2015 Jeffry A. Borror/ q4m LLC