One of the first things I was told by my ex boss was to never use for/while loops unless it’s a matter of life and death. And he was dead serious. I had just taken two c++ classes in college so wasn’t sure how I would be able to replace for/while loops. They are easy to use and make your life easier. BUT that’s not the case in q. There are better alternatives there. Thing to remember with q is that it loves lists. If there is any operation where you can use lists, then do that!
In this post, I am going to cover two adverbs, scan and over, that you can use instead of for/while loops.
Scan – \
The scan
adverb is denoted by \
. You can also use the keyword scan
. Scan
takes a dyadic function (that’s a function that takes two arguments) and applies each item on the right side and applies it to the item on the left side. With scan
, you get to see the result after each iteration.
For example,
q)1+\1 2 3 2 4 7
First, it takes 1+1
and returns 2
. Then, it takes that value (2
) and adds it to next value on the list (2
) to return 4
. Finally, it takes 4
and adds it to 3
to return 7
.
In this simple example, +
is the dyadic function being applied to left operand (1
) and right operand (1 2 3
). You can use a custom function instead of +
for more practical purposes. For example:
q)4{5*x-y}\1 2 3 15 65 310
In this example, my custom function is {5*x-y}
. This function takes the x
value (4
), subtracts y
(1 2 3
) from it and then multiplies it by 5
. It does this for each value in the list (1 2 3
). So, 4-1
is 3
and then 3*5=15
.
Over – /
Over
and scan
are very similar. The difference is that instead of giving you result after each iteration, over
just returns you the last value.
Let’s look at the same examples as above and compare the two:
q)1+\1 2 3 2 4 7 q)1+/1 2 3 7 q)4{5*x-y}\1 2 3 15 65 310 q)4{5*x-y}/1 2 3 310
As you can see, over
simply returns the last value.
Different syntax
All the stuff I said above, you can find on kx website and q for mortals book. However, during a training session by David Demner (very smart guy), I was introduced to another way of using scan
and over
. I had forgotten about it but my colleague William Wicker reminded me of it (thanks Bill!).
The new syntax gives you an option to provide a seed and number of iterations to do as well as provide a function instead if number of iterations is not concrete. For example, let’s say I want to run the function {5*y}
5x times with the first value being 1
:
q){5*x}\[5;1] 1 5 25 125 625 3125
So, what is it doing? It takes first value as 1
. Then it takes that and multiples by 5
which returns 5
. Then it takes 5
and multiples by 5
to give 25
and it does that 5x times…so overall you get list with 6
values.
If you use over
instead, you will just get the last value, 3125
.
q){5*x}/[5;1] 3125
This behavior is only valid for monadic functions. If you tried doing this with our previous dyadic function {5x*y}
, you will get a different result.
q){5*x-y}\[5;1] 20
For a dyadic function, it takes 5
as the x
value and 1
as the y
value. 5-1
is 4
and then 5*4
is 20
. You can have lists as well.
q){5*x-y}\[5 6;1] 20 25 q){5*x-y}\[5 6;1 2] 20 25 90 115 q){5*x-y}\[5 6;1 2 3] 20 25 90 115 435 560 q){5*x-y}\[5 6 7;1 2 3] 20 25 30 90 115 140 435 560 685 q){5*x-y}/[5 6 7;1 2 3] 435 560 685
Let’s go back to our monadic function {5*x}
and instead of running the function for a specific number of iterations, we will run it as long as a condition is true.
q)f:{5*x} q)f\[{f[x]>400};1] 1 5 25 125
Here, I first defined our function as f and then in the iteration part, defined another function which checks for condition f[x]>400
. Changing that condition will change the result as well:
q)f\[{f[x]>800};1] 1 5 25 125 625
That’s it for now. Whenever you feel the urge to use a do
/while
loop, just remember that there might be a better alternative out there.
Credits: Thanks to David Demner for clearing out some doubts for me.