K Language: Upper Case

In the K Room on APL Farm on Matrix yesterday, someone noted that K has _ to transform a string to lower case, but no corresponding built-in for upper case.

That prompted me to take a moment to see if I could write an upper function to do this.

The following is my K session, unedited except to add comments with explanations of what I was trying and thinking. I hope this helps others learning array programming or K in particular:

# (1) I run K at the CLI:
$ k
ngn/k, (c) 2019-2023 ngn, GNU AGPLv3. type \ for more info
 \+  / (2) I immediately look at the help with `\+`
Verbs:    : + - * % ! & | < > = ~ , ^ # _ $ ? @ . 0: 1:
notation: [c]har [i]nt [n]umber(int|float|char) [s]ymbol [a]tom [d]ict
          [f]unc(monad) [F]unc(dyad) [xyz]any
special:  var:y     set    a:1;a -> 1
          var::y    global a:1;{a::2}[];a -> 2
          (v;..):y  unpack (b;(c;d)):(2 3;4 5);c -> 4
          :x        return {:x+1;2}[3] -> 4
          :[x;y;..] cond   :[0;`a;"\0";`b;`;`c;();`d;`e] -> `e
          o[..]     recur  {:[x<2;x;+/o'x-1 2]}9 -> 34
          [..]      progn  [0;1;2;3] -> 3

::  self      f:(::);f 12 -> 12
 :  right     f:(:);f[1;2] -> 2   "abc":'"d" -> "ddd"
 +x flip      +("ab";"cd") -> ("ac";"bd")
N+N add       1 2+3 -> 4 5
 -N negate    - 1 2 -> -1 -2
N-N subtract  1-2 3 -> -1 -2
 *x first     *`a`b -> `a   *(0 1;"cd") -> 0 1
N*N multiply  1 2*3 4 -> 3 8
 %N sqrt      %25 -> 5.0   %-1 -> 0n
N%N divide    2 3%4 -> 0.5 0.75
 !i enum      !3 -> 0 1 2   !-3 -> -3 -2 -1
 !I odometer  !2 3 -> (0 0 0 1 1 1;0 1 2 0 1 2)
 !d keys      !`a`b!0 1 -> `a`b
 !S ns keys   a.b.c:1;a.b.d:2;!`a`b -> `c`d
x!y dict      `a`b!1 2 -> `a`b!1 2
i!I div       -10!1234 567 -> 123 56
i!I mod       10!1234 567 -> 4 7
 &I where     &3 -> 0 0 0   &1 0 1 4 2 -> 0 2 3 3 3 3 4 4
 &x deepwhere &(0 1 0;1 0 0;1 1 1) -> (0 1 2 2 2;1 0 0 1 2)
N&N min/and   2&-1 3 -> -1 2   0 0 1 1&0 1 0 1 -> 0 0 0 1
 |x reverse   |"abc" -> "cba"   |12 -> 12
N|N max/or    2|-1 3 -> 2 3   0 0 1 1|0 1 0 1 -> 0 1 1 1
 <X ascend    <"abacus" -> 0 2 1 3 5 4
 >X descend   >"abacus" -> 4 5 3 1 0 2
 <s open      fd:<`"/path/to/file.txt"
 >i close     >fd
N<N less      0 2<1 -> 1 0
N>N more      0 1>0 2 -> 0 0
 =X group     ="abracadabra" -> "abrcd"!(0 3 5 7 10;1 8;2 9;,4;,6)
 =i unitmat   =3 -> (1 0 0;0 1 0;0 0 1)
N=N equal     0 1 2=0 1 3 -> 1 1 0
 ~x not       ~(0 2;``a;"a \0";::;{}) -> (1 0;1 0;0 0 1;1;0)
x~y match     2 3~2 3 -> 1   "4"~4 -> 0   0~0.0 -> 0
 ,x enlist    ,0 -> ,0   ,0 1 -> ,0 1   ,`a!1 -> +(,`a)!,,1
x,y concat    0,1 2 -> 0 1 2  "a",1 -> ("a";1)
d,d merge     (`a`b!0 1),`b`c!2 3 -> `a`b`c!0 2 3
 ^x null      ^(" a";0 1 0N;``a;0.0 0n) -> (1 0;0 0 1;1 0;0 1)
a^y fill      1^0 0N 2 3 0N -> 0 1 2 3 1   "b"^" " -> "b"
X^y without   "abracadabra"^"bc" -> "araadara"
 #x length    #"abc" -> 3   #4 -> 1   #`a`b`c!0 1 0 -> 3
i#y take      5#"abc" -> "abcab"   -5#`a`b`c -> `b`c`a`b`c
X#d take keys `c`d`f#`a`b`c`d!1 2 3 4 -> `c`d`f!3 4 0N
I#y reshape   2 3#` -> (```;```)
f#y replicate (3>#:')#(0;2 1 3;5 4) -> (0;5 4)   {2}#"ab" -> "aabb"
 _n floor     _12.34 -12.34 -> 12 -13
 _c lowercase _"Ab" -> "ab"
i_Y drop      2_"abcde" -> "cde"   -2_`a`b`c -> ,`a
X_d drop keys `a`c_`a`b`c!0 1 2 -> (,`b)!,1
I_Y cut       2 4 4_"abcde" -> ("cd";"";,"e")
f_Y weed out  (3>#:')_(0;2 1 3;5 4) -> ,2 1 3
X_i delete    "abcde"_2 -> "abde"
 $x string    $(12;"ab";`cd;+) -> ("12";(,"a";,"b");"cd";,"+")
i$C pad       5$"abc" -> "abc  "   -3$"a" -> "  a"
s$y cast      `c$97 -> "a"   `i$-1.2 -> -1   `$"a" -> `a
s$y int       `I$"-12" -> -12
 ?X distinct  ?"abacus" -> "abcus"
 ?i uniform   ?2 -> 0.6438163747387873 0.8852656305774402 /random
X?y find      "abcde"?"bfe" -> 1 0N 4
i?x roll      3?1000 -> 11 398 293   1?0 -> ,-8164324247243690787
i?x deal      -3?1000 -> 11 398 293 /guaranteed distinct
 @x type      @1 -> `i   @"ab" -> `C   @() -> `A   @(@) -> `v
x@y apply(1)  {x+1}@2 -> 3   "abc"@1 -> "b"   (`a`b!0 1)@`b -> 1
 .S get       a:1;.`a -> 1   b.c:2;.`b`c -> 2
 .C eval      ."1+2" -> 3
 .d values    .`a`b!0 1 -> 0 1
x.y apply(n)  {x*y+1}. 2 3 -> 8   (`a`b`c;`d`e`f). 1 0 -> `d

@[x;y;f]   amend  @["ABC";1;_:] -> "AbC"   @[2 3;1;{-x}] -> 2 -3
@[x;y;F;z] amend  @["abc";1;:;"x"] -> "axc"   @[2 3;0;+;4] -> 6 3
.[x;y;f]   drill  .[("AB";"CD");1 0;_:] -> ("AB";"cD")
.[x;y;F;z] drill  .[("ab";"cd");1 0;:;"x"] -> ("ab";"xd")
.[f;y;f]   try    .[+;1 2;"E:",] -> 3   .[+;1,`;"E:",] -> "E:'type\n"
?[x;y;z]   splice ?["abcd";1 3;"xyz"] -> "axyzd"
 `c$97              / (3) I try out the example from the help `c$97 to make sure it works as expected
"a"
 `c$65              / (4) I edit the example with 65 which I happen to know is the ASCII code for capital A
"A"
 "abc"              / (5) I consider "abc" as my example string to test with
"abc"
 s                  / (6) I verify whether s is already bound
'value              / (7) (It's not)
 s
 ^

 s:"abc"            / (8) I assign s the value "abc"
 `i$s               / (9) I convert s to integers using `i$s (I'll realize later this was unnecessary)
97 98 99
 -32+`i$s           / (10) I add -32 to that list
65 66 67
 `c$-32+`i$s        / (11) I convert the integers to characters using `c$
"ABC"
 upper:`c$-32+`i$:  / (12) I try to define upper with a final : thinking that will make this a tacit expression
'parse              / (13) (It fails to parse)
 upper:`c$-32+`i$:
                  ^

 upper:`c$-32+`i$   / (14) I remove the final : and it works. Yay!
 upper "abc"
"ABC"               / (15) (I think about whether the initial cast from characters to integers is necessary.)
 1+"abc"            / (16) I add 1 to my string "abc" and realize I didn't need to cast.
98 99 100
 upper:`c$-32+      / (17) I revise my upper implementation to not cast from characters to integers
 upper "abc"
"ABC"               / (18) (I realize that my implementation assumes that all characters in the argument are already lower case.)
 upper:`c$-32+_     / (19) I revise my upper implementation to first put the input string into all lower case.
 upper "aBcDe"
`c$-32+_["aBcDe";]  / (20) I try out my new upper implementation. Instead of a string, it returns a projection.
                    / (21) (I realize that, for a tacit expression, I need to explicitly use the monadic arity of _)
 upper:`c$-32+_:    / (22) I revise my upper implementation to use the monadic arity of _ by appending : to it.
 upper "aBcDe"
"ABCDE"             / (23) Success!

Why -32+ instead of subtracting 32?

Given K and array languages are evaluated overall right to left, it is more concise to chain further processing on the left. Not doing so often requires parentheses to control order of execution, and in K can force the use of a function literal due to K's more spartan tacit programming features.

Here is an alternative that demonstrates this, (thanks to @coltim:matrix.org for helping me clean up my initial attempt):

upper:{`c$(_x)-32}

Many times a function literal is more readable; in this simple case, I feel the tacit definition is equally straightforward. Since K's tacit facilities are simple compared to J's or Dyalog APL's, parsing tacit expressions in K requires far less mental exertion.


Tags: k-language strings point-free-style tacit-programming array-programming ngn-k

Copyright © 2024 Daniel Gregoire