Entries in functional form (1)
How do I use the functional form of . (dot) apply?
Sunday, February 26, 2012 at 10:20PM Short answer:
1. .[container; indices; function] 2. .[container; indices; function; second_args]
(Note that there is another overload for . with three arguments, called protected execution, which is invoked when the first argument to . 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
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)
