cancel
Showing results for 
Search instead for 
Did you mean: 

Underneath q is k

dhodgins
New Contributor III
New Contributor III

The q operators and functions are a combination of optimized c functions and wrappers on top of the k language.

k preceded q by about a decade. These days you are supposed to write q code so k is not documented anymore, unfortunately parse trees and debugging require some knowledge of k.

So how do we translate between the 2 languages?

The .q namespace contains all the bits of q which are are wrappers of k and hence gives us a good starting point.

If we type lj we can see its definition, .q.lj is the fully qualified name.

 

q)lj
k){$[$[99h=@y;(98h=@!y)&98h=@. y;()~y];.Q.ft[,\:[;y];x];'"type"]}

 

In order to understand this we need to know what @ and ! mean in k.

So lets take .q namespace, and filter out all the functions, this gives us a good starting point for translating q into k.(I have manually rearranged the results to group similar things together)

 

\c 2000 200
q)where[1_not type'[.q]in -10 100 106 110h]#.q
neg       | -:          
not       | ~:          
hdel      | ~:          
null      | ^:          
string    | $:          
mmu       | $           
reciprocal| %:          
ltime     | %:          
floor     | _:          
and       | &           
or        | |           
lsq       | !           
inv       | !:          
key       | !:          
count     | #:          
first     | *:          
reverse   | |:          
distinct  | ?:          
group     | =:          
where     | &:          
flip      | +:          
type      | @:          
get       | .:          
value     | .:          
hclose    | >:          

sums      | +\          
prds      | *\          
mins      | &\          
maxs      | |\          
fills     | ^\          
deltas    | -':         
ratios    | %':         
raze      | ,/          

read0     | 0::         
read1     | 1::         
ceiling   | -_-:        
prev      | :':         
union     | ?,          
differ    | $["b"]~~':  
all       | min$["b"]   
any       | max$["b"]   
upsert    | .[;();,;]   
hsym      | $["s"]!'[-1]
system    | .,["\\"]    

md5       | ![-15]      
attr      | ![-2]       
hcount    | ![-7]       
eval      | ![-6]       
reval     | ![-24]      

 

There are 5 categories. We already excluded lambdas, aliases of internals like -15!, projections like ceiling(neg floor neg@), named adverb'ed operators like sums, and the k operators.

Both get and value map to the same k operator which is why they are used interchangeably when people write code. Many of the operators are overloaded like ! which does both inv and key depending on the input.

In k we use a trailing : to indicate that the operator is being used in its monadic form. The : is only required when the statement is ambiguous, the alternative to using : is to use @ or () eg:

 

q)k)3=4 /dyadic
0b
q)k)3=4 3 4 5 4 /dyadic
01000b
q)k)=:4 3 4 5 4 /monadic explicit
4| 0 2 4
3| ,1
5| ,3
q)k)=4 3 4 5 4 /monadic implicit
4| 0 2 4
3| ,1
5| ,3
q)k)`a`b`c`d`e=4 3 4 5 4 /used dyadically even though only the monadic form makes sense
'type
  [0]  k)`a`b`c`d`e=4 3 4 5 4
                     ^
q)k)`a`b`c`d`e=:4 3 4 5 4 /: can't be used like this as it like an inplace modification(like +:)
'assign
  [0]  k)`a`b`c`d`e=:4 3 4 5 4
                     ^
q)k)`a`b`c`d`e =:4 3 4 5 4 /Space doesn't help
'assign
  [0]  k)`a`b`c`d`e =:4 3 4 5 4
                      ^
q)k)`a`b`c`d`e@=4 3 4 5 4 /We can explicitly index using @ or []
4| `a`c`e
3| ,`b
5| ,`d
q)k)`a`b`c`d`e(=:)4 3 4 5 4 /We can use round brackets and :
4| `a`c`e
3| ,`b
5| ,`d
q)k)`a`b`c`d`e(=)4 3 4 5 4 /Or just round brackets
4| `a`c`e
3| ,`b
5| ,`d

 

There are a few k operators missing from .q because they have no alias.

 

q)enlist 3
,3
q)k),3
,3

 

enlist and , are similar in what they do, but enlist is written in c. enlist is also variadic unlike ,

 

q)enlist[3;4;5]
3 4 5
q)k),[3;4;5]
'rank
  [0]  k),[3;4;5]

 

Some of the k operators have been wrapped into functions to add more protection/flexibility. eg til is actually ! but it checks the argument is the correct type.

 

q)til
k){$[0>@x;!x;'`type]}
q)til`a
q)til 1 2
'type
  [0]  til 1 2
       ^
q)k)!1 2
`long
q)k)!1 2h
`short

 

upsert and set are very similar:

 

q)upsert
.[;();,;]
q)set
k){$[@x;.[x;();:;y];-19!((,y),x)]}

 

upsert appends with , whereas set overwrites with :. set also checks the type of the input and compresses it with -19! if you give a list as the left hand argument

As q has a many to one relationship with k it is difficult to reverse the translation, but we can generate a rough lookup table:

 

q)group where[1_not type'[.q]in -10 100 106 110h]#.q
-:          | ,`neg
~:          | `not`hdel
^:          | ,`null
$:          | ,`string
%:          | `reciprocal`ltime
_:          | ,`floor
-_-:        | ,`ceiling
&           | ,`and
|           | ,`or
$           | ,`mmu
!           | ,`lsq
!:          | `inv`key
![-15]      | ,`md5
#:          | ,`count
*:          | ,`first
min$["b"]   | ,`all
max$["b"]   | ,`any
+\          | ,`sums
*\          | ,`prds
&\          | ,`mins
|\          | ,`maxs
^\          | ,`fills
-':         | ,`deltas
%':         | ,`ratios
$["b"]~~':  | ,`differ
:':         | ,`prev
|:          | ,`reverse
?:          | ,`distinct
=:          | ,`group
&:          | ,`where
+:          | ,`flip
@:          | ,`type
.:          | `get`value
![-2]       | ,`attr
.[;();,;]   | ,`upsert
,/          | ,`raze
?,          | ,`union
0::         | ,`read0
1::         | ,`read1
>:          | ,`hclose
$["s"]!'[-1]| ,`hsym
![-7]       | ,`hcount
.,["\\"]    | ,`system
![-6]       | ,`eval
![-24]      | ,`reval

 

In order to include all the functions such as lj, it is even simpler but i won't print the whole output for brevity.

 

q)group 1_.q
-:| ,`neg
~:| `not`hdel
^:| ,`null
$:| ,`string
%:| `reciprocal`ltime

 

If we look at the .Q namespace, we can see it is all written in k. In .Q.ps which is the function that runs select queries on partitioned and segmented tables we can see that in k we can use either newline(\n) or ; to terminate a statement, hence the 4 lines don't end in ; as you might have expected.

 

q).Q.ps
k){[t;c;b;a]if[-11h=@t;t:. t];if[~qe[a]&qe[b]|-1h=@b;'`nyi];d:pv;v:$[q:0>@b;0;~#b;0;-11h=@v:*. b;pf~*`\:v;0]
 if[$[~#c;0;@*c;0;-11h=@x:c[0]1;pf~*`\:x;0];d@:&-6!*c;c:1_c]
 if[$[#c;0;(g:(. a)~,pf)|(. a)~,(#:;`i)];f:!a;j:dt[d]t;if[q;:+f!,$[g;?d@&0<j;,+/j]];if[v&1=#b;:?[+(pf,f)!(d;j)[;&0<j];();b;f!,(sum;*f)]]]
 if[~#d;d:pv@&pv=*|pv;c:,()];f:$[q;0#`;!b];g:$[#a;qa@*a;0]
 $[(1=#d)|$[q;~g;u&pf~*. b];$[~q;.q.xkey[f];b;?:;::]foo[t;c;b;a;v]d;(?).(foo[t;c;$[q;()!();b];*a;v]d;();$[q;0b;f!f];*|a:$[g;ua a;(a;$[#a;(,/;)'k!k:!a;()])])]}

 

The other difference between k and q are the rules around what constitutes a symbol. I'm unsure whether there is a reasonable justification for this. Personally I think it is tupid.

Often when you need help or want to debug you take a variable(or copy the error) and copy it into another q process or skype|teams|outlook however -3! prints in k format and k doesn't allow _ in symbols so you have to cast them from strings.

 

q)tab:([]f:`:dave_hodgins.txt`:jim_bob.txt;("london";"wherever");4 5)
q)tab
f                 x          x1
-------------------------------
:dave_hodgins.txt "london"   4
:jim_bob.txt      "wherever" 5
q)-3!tab
"+`f`x`x1!(`:dave_hodgins.txt`:jim_bob.txt;(\"london\";\"wherever\");4 5)"

/Need to unescape the double quotes
q)k)+`f`x`x1!(`:dave_hodgins.txt`:jim_bob.txt;(\"london\";\"wherever\");4 5)
'(
  [0]  k)+`f`x`x1!(`:dave_hodgins.txt`:jim_bob.txt;(\"london\";\"wherever\");4 5)
k)+`f`x`x1!(`:dave_hodgins.txt`:jim_bob.txt;(\"london\";\"wherever\");4 5)
^

q)-1@-3!tab;
+`f`x`x1!(`:dave_hodgins.txt`:jim_bob.txt;("london";"wherever");4 5)

/need to cast to symbol due to different rules
q)k)+`f`x`x1!(`:dave_hodgins.txt`:jim_bob.txt;("london";"wherever");4 5)
'bob.txt
  [0]  k)+`f`x`x1!(`:dave_hodgins.txt`:jim_bob.txt;("london";"wherever");4 5)
                                             ^

/Ugh, so ugly!
q)k)+`f`x`x1!(`$(":dave_hodgins.txt";":jim_bob.txt");("london";"wherever");4 5)
f                 x          x1
-------------------------------
:dave_hodgins.txt "london"   4
:jim_bob.txt      "wherever" 5

 

Finally you can also write k inside q code by surrounding it with round brackets, or by using square brackets:

 

q)`a`b!(1 2;3 4)
a| 1 2
b| 3 4
q)flip`a`b!(1 2;3 4)
a b
---
1 3
2 4
/q doesn't know what +: is
q)+:`a`b!(1 2;3 4)
'+:
  [0]  +:`a`b!(1 2;3 4)
        ^
q)(+:)`a`b!(1 2;3 4)
a b
---
1 3
2 4
q)+:[`a`b!(1 2;3 4)]
a b
---
1 3
2 4

 

2 REPLIES 2

jbetz34
New Contributor II

jbetz34
New Contributor II

q is built on k4, but shakti (k9) has many similar aspects to the k4 language and is better documented here: https://estradajke.github.io/k9-simples/k9/index.html