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)