Mapping a unary function to a list - why is `each` not always required?

Viewed 148

I am going through a tutorial on q. Here is the initial setup.

buys:2 1 4 3 5 4
sells:2 4 3 2

I then defined the following function

f:{x & (sums buys)}

I tried to use it in

f (sums sells)

But I get the following error

'length
  [1]  f:{x & (sums buys)}

I note that if instead I do this, it works

q)f each (sums sells)
2 2 2 2  2  2
2 3 6 6  6  6
2 3 7 9  9  9
2 3 7 10 11 11

However, the following unary function outputting a list has no problem being applied

q){(x*x;x)} (1 2 3)
1 4 9
1 2 3

The type signature of the 2 functions seems to be the same to me. Why does one get evaluated while the other raises an error?

3 Answers

Your 'length error is due to trying to perform the & operation with two lists which are not the same length. We can inspect the variables in the debug session that q will drop you into if you run this on the REPL.

q)f (sums sells)
'length
  [1]  f:{x & (sums buys)}
            ^
q))x
2 6 9 11
q))buys
2 1 4 3 5 4

& requires both lists to be the same length, or one input to be a list and one to be an atom, so it can do item by item comparison. With that in mind we can see that

f each (sums sells)

Works because now we are stepping through our sums sells list and comparing one item at a time to our buys list.

For the case of your unary function, this works because the * operation is being applied to two integer lists of the same length, so it will always work.

You can read more on functions (and the underlying vector ideas) here https://code.kx.com/q4m3/6_Functions/

It's detailed somewhat in q for mortals here: https://code.kx.com/q4m3/4_Operators/#45-greater-and-lesser

Specifically "Being atomic they operate item-wise on lists...."

Generally, kdb/q wants to do list operations itemwise, e.g.

q)1 2+2 3
3 5

If you give it lists of different length:

q)1 2+1 2 3
'length
  [0]  1 2+1 2 3
          ^

it doesn't know if you want to do

q)1 2+\:1 2 3
2 3 4
3 4 5

or

q)1 2+/:1 2 3
2 3
3 4
4 5

Same applies to & in your case.

Your other function is not comparable because the only itemwise operation it does x*x is on lists of the same length. So that's ok.

Another way to hack it compare only first four(or whatever number) values f:{x & (sums buys[till count sells])}

Related