Download K from the Kx Systems web site. This takes about one minute and includes a powerful database system.
Now that you have the software, please execute the examples as you go.
The designer of the system has many quirks.
Conciseness and lots of power with few keystrokes is one of them.
2+3 3*2 / Right to left precedence: 3*2+5 / yields 21 / Assignment abc: 3*15 cdf: 15% 3 / division is percent sign x: 5 3 4 10 / scalar-vector operations 3*x
/ prefix sum +\3*x / element by element operations x*x / cross-product of all elements / read this "times each right each left" x*/:\:x / generating data y: !10 / multiplication table, each left, each right (cross-product) y*\:/:y `show $ `y y:y*y / variable multiplication causes gui representation to be updated. / Try updating the gui and they will be updated.
/ tables n: 1000 names: `bob `ted `carol `alice emp.salary: 20000 + n _draw 10000 emp.name: names[n _draw #names] emp.manager: names[n _draw #names] `show $ `emp / modify salary to be twice the salary emp.salary: 2*emp.salary / create a chart emp.salary..c:`chart `show $ `emp.salary
/ Dot product (multiply element by element and then add up) v: 1 3 5 w: 2 6 4 dot:{[v1; v2] +/ v1 * v2}
n: 100 x: (n*n) _draw 10000 m1: (n;n) # x / note that n n # x won't work because n n is function app x: (n*n) _draw 10000 m2: (n;n) # x dot:{[x;y] +/x*y} r: m1 dot/:\: +m2 / dot product with the transpose.
/:\:is important. Reversing the order gives the transpose. This order causes dot to multiply the top row times all the columns first (forming the first row of the result).
sal: 200 250 220 100 f:{[x] :[x < 215; x*1.1; x*1.2]} sal: f'sal
seq1: "abcde" seq2: "012" seq1 ,\: seq2
seq1: "abcde" seq2: "012" seq1 ,/: seq2
seq1: "abcde" seq2: "012" seq1 ,\:/: seq2 / note contrast with seq1 ,/:\: seq2 / This makes seq2 vary more frequently than seq1.
purchase.date: (!10), (|!5) purchase.item: (10 # `candy), (5 # `toys) purchase.qty: 100 + 15 _draw 400 purchase.price: 20 + !15form the itempurchase table which holds each item with all the purchase, qty and price information.
itempurchase.item: ? purchase.item / remove duplicates part: = purchase.item / gives itemsets in the same order as above itempurchase.qtys: purchase.qty[part] itempurchase.price: purchase.price[part] itempurchase.date: purchase.date[part]
k server.k -i 1234
retval: (`machinename; 1234) 4: (`functname; (`param1; `param2))Thus, one is calling the other machine with two arguments: a function name and a list of parameters.
k client
Working in a single language reduces errors, increases
speed yet further, and is more fun because you can
concentrate on the algorithm.
(Note: Many errors and much overhead comes from integrating different
languages, e.g. C/Perl/Sybase/GUI.)
k
"a"
"Hello, world!"
`Hello `"Hello, world!" / need the quotes because of the space
15 16.2
"a" `fun 2 3.2
4 2 5 67 2 (4;2; 5; 67; 2) / equivalent to the above `good `better `best (`good; `better; `best) / equivalent to the above "good" / a sequence of characters is a vector ("g";"o";"o";"d") / a sequence of characters is a vector
("hello"; `hello; 15; `abc)
x: 3 5 2 17 2 x[0] x[3]
x , x
5 ,5
comp.name: `zurich `aetna `"met life" comp.assets: 43 23 35 `show $ `comp
For example, vertical bar is max when dyadic and reverse when used monadically.
4 | 5 32 64 | 21 89 | 32 64 21 89
Another example occurs with asterix.
4 5 * 2 3 / multiplication * 4 5 2 3 / first
"foobar" 0: "Hello, world!"
The 0: (zero colon) operator writes character vectors to the file. Lists of charactor vectors are separated by a newline. Try an example on your own.
One can also read in a file, but in that case the file name is on the right side.
a: 0: "foobar" / comes back as list whose zeroth element is string
"foosymb" 1: `"Hello, world!" / write a symbol to a fileThis can be read out:
a: 1: "foosymb" a / you will see the symbol hello world.
"foo" 5: (`op1; "arg1"; `arg2)file "foo" will be extended by three more elements. But this is not good since the end of this operation does not have a clear boundary.
Therefore, it is better to say
"foo" 5: ,(`op1; "arg1"; `arg2)because that appends a single element consisting of the list
(`op1; "arg1"; `arg2).
k foo abc d efwill put into the variable _i within the foo program the strings "abc", "d", and "ef".
x: !100
Generate 1000 numbers between 0 and 18 with replacement.
a: 1000 _draw 19Generate 19 numbers between 0 and 18 without replacement. ( Warum nur neunzehn?)
a: 19 _draw -19 / generate 1000 numbers between 0 and 18 without replacementOne more kind of random number: random numbers between 0 and 1.
b: 100 _draw 0
5 + 3 5 6 7 - 10 20 30 5 6 7 * 3 6 7 8 9 % 2
Ok, you may not be used to having the percent sign be used for division, but you'll learn to like it.
4 > 2 4 6 > 2 8 (!10) > 7 4 > !10
_tthat gives the number of seconds after Jan 1, 2035, which is a Monday. (So, this number is currently negative.)
_ltime _t
_gtime _t
* _gtime
days: `Mon `Tues `Wed `Thu `Fri `Sat `Sun days @ (_ _t%86400) ! 7
var:{[x] a: avg[x*x] b: avg[x] a - ( b * b)}
n: 100 x: (n*n) _draw 10000 m1: (n;n) # x / note that n n # x won't work because n n is function app x: (n*n) _draw 10000 m2: (n;n) # x dot:{[x;y] +/x*y} r: m1 dot/:\: +m2 / dot product with the transpose.
/:\:is important. Reversing the order gives the transpose. This order causes dot to multiply the top row times all the columns first (forming the first row of the result).
seq1: "abcde" seq2: "012" seq1 ,\: seq2
seq1: "abcde" seq2: "012" seq1 ,/: seq2
seq1: "abcde" seq2: "012" seq1 ,\:/: seq2 / note contrast with seq1 ,/:\: seq2 / This makes seq2 vary more frequently than seq1.
x: 5 y: 7 if[x < y min: x max: y ] if[~ x < y min: y max: x ]
min: :[x < y; x; y] / if statement follows condition and else statement comes after. max: :[x < y; y; x]
n: #arr i:0 out:() while[ i < n out,: f[arr[i]] i+: 1 ] out
input: 100 _draw 5 n: _ (#input) % 2 arr: n # 0 / initialize i: 0 do[n arr[i]: input[i] i+: 1 ]
input: 100 _draw 5 n: _ (#input) % 2 arr: input[!n]
arr: n # input
sal: 200 250 220 100 f:{[x] :[x < 215; x*1.1; x*1.2]} sal: f'sal
ranks: `pres `vicepres `dilbert emp.rank: ranks[100 _draw 3] emp.salary: 50000 + 100 _draw 950000
g:{[rank; salary] :[rank = `vicepres salary * 1.1 salary*1.05]} emp.salary: g'[emp.rank; emp.salary]
i: & emp.rank = `vicepres / vice-presidents j: & ~ emp.rank = `vicepres / non-vice presidents emp.salary[i]*: 1.1 emp.salary[j]*: 1.05
purchase.date: (!10), (|!5) purchase.item: (10 # `candy), (5 # `toys) purchase.qty: 100 + 15 _draw 400 purchase.price: 20 + !15form the itempurchase table which holds each item with all the purchase, qty and price information.
itempurchase.item: ? purchase.item / remove duplicates part: = purchase.item / gives itemsets in the same order as above itempurchase.qtys: purchase.qty[part] itempurchase.price: purchase.price[part] itempurchase.date: purchase.date[part]
fact:{[n] if[n < 2 :1 ] :n*fact[n-1] }
fact:{[n] :[n > 0 n*fact[n-1] / the initial : is a return 1] }
fact:{[n] :[n > 0 n*_f[n-1] 1] }
f:{[x] x*x} str: "f[5]" . str
deleteallblanks:{[s] x: s = " " / gives binary 1 or zero saying whether s is blank or not. i: & ~ x / gives all indices that are not blank s[i]}
/ returns one if x is a subset of y subset:{[x;y] (#y) > |/ y ?/: x} /finds intersection of two lists intersect: {[x;y] x,: () y,: () if[(#x) < (#y) i: x ?/: y j: & i < #x :x[?i[j]] ] i: y ?/: x j: & i < #y :y[?i[j]] } /finds indexes in x and y that intersect / If x and y are both sets, then the results will be of the same length / fastest of all intersectindexes: {[x;y] i: x ?/: y / where each y hits j: & i < #x / those ys that hit :(i[j];j) }
avg:{(+/ x) % # x} var:{avg[_sqr x] - _sqr avg[x]} std:{_sqrt var[x]} cov:{avg[x * y] - avg[x] * avg[y]} corr:{ (cov[x;y])%((std[x]) * (std[y]))}
/ An earliest deadline first scheduler. / Organize tasks by their deadlines and then see if there / is enough time to execute all of them before their deadlines. / APPLICATION-SPECIFIC edd:{[tasks; dead] i: < dead tasks@: i completiontimes: +\ tasks dead@: i : &/ ~ tasks > dead } / DATA tasktimes: 2 3 1 4 2 deadlines: 4 9 8 8 19 / EXECUTION edd[tasktimes; deadlines] / returns 1 if all are schedulable and 0 otherwise
k server.k -i 1234
retval: (`machinename; 1234) 4: (`functname; (`param1; `param2))Thus, one is calling the other machine with two arguments: a function name and a list of parameters.
/ factorial fact:{[n] :[n > 0 :n*fact[n-1] :1] } / quadratic formula quad:{[a;b;c] x: ((-b) + _sqrt((b^2) - 4*a*c)) % (2*a) y: ((-b) - _sqrt((b^2) - 4*a*c)) % (2*a) :(x;y) } / responds to messages one at a time; / buffering is automatic. / list[0] is the function. list[1] is the list of arguments. .m.g:{[list] args: list[1] if[list[0] = `fact :fact[args[0]] ] if[list[0] = `quad :quad[args[0]; args[1]; args[2]] ] }
machine: _h / illustrates case where client and server / run on the same machine machine: ` ret: (machine; 1234) 4: (`fact; ,5) ret / should be 120 ret: (machine; 1234) 4: (`quad; (1; 4; 0)) ret / should be 0 -4
k den2 -i 1235
java den2
/ Calling KSQL from K \l db / running from K, just load the library `"build stock" stock.name: `Alcatel `Alstom `"Air France" `"France Telecom" `"Snecma" `Supra stock.industry: `telecom `engineering `"aviation" `"telecom" `"aviation" `consulting .d.r"stock: 'name'key stock" .d.r"stock['Alcatel'].industry" `"build trade" n:1000 / number of trades t1: ,/("trade:([]" " stock:n rand stock.name," " price:50 + .25 * n rand 200," " amount:100 * 10 + n rand 20," " date:date['1998-01-01'] + n rand 449)") .d.r t1 t2: "trade:'date' asc trade" .d.r t2 .d.r "show'trade'" / now it's time to calculate 6 month hi, low, average, volume / for a given stock. getstats:{[name;date] x: " select first stock, hi: max price, low: min price, " x,: " avgprice: avg price, vol: sum amount " x,: " from trade where stock = '" x,: ($name) x,: "', date within (date['" x,: ($date) x,: "'], date['" x,: ($date) x,: "'] + 182)" res: .d.r x :res } res: getstats["Alcatel";"1998-01-01"] .d.r "show'res'"
The first line is just a comment. K uses slashes instead of pound signs.
/ Calling KSQL from K
\l db / running from K, just load the library
`"build stock" stock.name: `Alcatel `Alstom `"Air France" `"France Telecom" `"Snecma" `Supra stock.industry: `telecom `engineering `"aviation" `"telecom" `"aviation" `consulting
.d.r"stock: 'name'key stock"
.d.r"stock['Alcatel'].industry"
`"build trade" n:1000 / number of trades t1: ,/("trade:([]" " stock:n rand stock.name," " price:50 + .25 * n rand 200," " amount:100 * 10 + n rand 20," " date:date['1998-01-01'] + n rand 449)") .d.r t1
t2: "trade:'date' asc trade" .d.r t2 .d.r "show'trade'"
/ now it's time to calculate 6 month hi, low, average, volume / for a given stock. getstats:{[name;date] x: " select first stock, hi: max price, low: min price, " x,: " avgprice: avg price, vol: sum amount " x,: " from trade where stock = '" x,: ($name) x,: "', date within (date['" x,: ($date) x,: "'], date['" x,: ($date) x,: "'] + 182)" res: .d.r x :res }
res: getstats["Alcatel";"1998-01-01"] .d.r "show'res'"
m: 500 do[m res: getstats["Alcatel";"1998-01-01"] ] ("These "), ($m), (" getstats requests required "),($ _t - start), (" seconds.")
.d.r "tradeseq: select price, amount, date by stock from trade"
\m i 1234
.m.g(the dots represent a kind of directory structure in the namespace; the first dot is the root).
/ Receive communication from another process .m.g:{[pair] x: getstatsseq[$pair[0]; $pair[1]] :x }
machine: ` / local machine server: 3: machine,1234 / declare a handle x: server 4: (`Alcatel; `"1998-01-01") `show $ `x
/ receive from web .m.h:{[string] i: string _ss "stock" string: *(i+6) _ string i: string ? "&" name: string[!i] s: #string date: string[(s-10) + !10] res: getstats[name;date] out: printheader[!res] vals: ,+res[] out,: ,/tablehtmlize[vals]'!#vals :out }