How do I use the functional form of . (dot) apply?

Short answer:

1. .[container; indices; function]
2. .[container; indices; function; second_args]

Note that there is another overload for . (apply) with three arguments, called protected execution, which is invoked when the first argument to . (apply) is a function or projection; protected execution is discussed in another faq.


In the 3-argument case, q applies function to those elements of container specified by indices, leaving the rest of container alone.

The behavior of . (apply) is very similar to that of @ (apply) (which is described in another faq). The only difference between @ (apply) and . (apply) is that the indices, in the case of . (apply), are applied at depth along the dimensions of the container. The following code behaves like . (apply) for a two-dimensional container:

To explore the different treatment of the indices argument between @ (apply) and . (apply), we’ll consider the simplest, multi-dimensional container: a two-dimensional list, aka a matrix.

q)matrix: 0N 4 # til 16
q)matrix
0  1  2  3
4  5  6  7
8  9  10 11
12 13 14 15
q)

We can use simple indexing of matrix with both @ (index) and . (index) to illustrate the difference:

q)@[matrix; 0]           // fetch row 0
0 1 2 3
q)@[matrix; 0 0]         // fetch matrix[0;0] fails.  why?
0 1 2 3                  // @ cannot index more
0 1 2 3                  // than one dimension
q)
q).[matrix; 0 0]         // dot indexes "at depth"
0
q)

Now let’s apply a function to selected elements from matrix using . (apply):

q).[matrix; (0; ::); 100+]
100 101 102 103
4   5   6   7
8   9   10  11
12  13  14  15
q).[matrix; 0 0; 100+]
100 1  2  3
4   5  6  7
8   9  10 11
12  13 14 15
q).[matrix; (::; 0); 100+]
100 1  2  3
104 5  6  7
108 9  10 11
112 13 14 15
q)

Notice that we used :: in order to elide a dimension from the indices.

We can also use . (apply) with dictionaries of lists:

q)dict: `a`b ! (1 2 3; 4 5 6)
q).[dict; (`a; 0); 50*]
a| 50 2 3
b| 4  5 6
q).[dict; (::; 1); 50*]
a| 1 100 3
b| 4 250 6
q)

The general case of applying a function at depth via functional . (apply) is not implemented for tables:

q)t: ([] a: `foo`bar; b: 5 10f)
q).[t; (0; `b); %[; 5]]
'nyi
q).[t; (`b; 0); %[; 5]]
'nyi
q)

The . form of apply has another trick up its sleeve: the empty list index. When the second parameter to . (apply) is (), the entire container is passed to function in a single invocation, as the following example demonstrates:

q).[matrix; (); 0N!]
(0 1 2 3;4 25 36 7;8 81 100 11;12 13 14 15)
0  1  2   3
4  25 36  7
8  81 100 11
12 13 14  15
q)

When using . (apply) in this manner, we can return anything from function; the type and shape do not matter:

q).[matrix; (); {"hello"}]
"hello"
q)

Returning to the second case, when function is dyadic, . (apply) takes a fourth argument (named second_args in our example), and each indexed element of container is paired with the corresponding element of second_args. This means that second_args must conform to indices (or be an atom). The following function expresses this behavior:

(Also see this faq on each-both and multivalent each.)

We can demonstrate how this works by altering the center square of matrix:

q).[matrix; (1 2; 1 2); *; (10 20; 100 200)]
0  1   2    3
4  50  120  7
8  900 2000 11
12 13  14   15
q)

kdbfaq

599 Words

2012-02-27 03:20 +0000