Five easy pieces #2: getting around

https://learninghub.kx.com/forums/topic/five-easy-pieces-2-getting-around

From a recent question on Stack Overflow:

q)q:9.638554216867471 
q)rnd[q;2;`up] / round up 
"9.64" 
q)rnd[q;2;`dn] / round down 
"9.63" 
q)rnd[q;2;`nr] / round to nearest 
"9.64" 
q)rnd[q+0 1 2;3;`up] 
"9.639" "10.639" "11.639"

Write rnd without any control words (do, while, if, Cond $). For bonus points extend to multiple modes (3rd argument):

q)rnd[q+0 1 2;3;`up`dn] 
"9.639" "10.639" "11.639" "9.638" "10.638" "11.638"

Does the job! Note the use of a dictionary where another language would need a control structure.

The each can be dispensed with. Both up and dn take vector arguments. .Q.f does not, so d[2] could be .Q.f’[nd;]:

 

q)rnd:{[x;nd;m] d:`up`dn`nr!(up[;nd];dn[;nd];.Q.f'[nd;]); (d m) x} 
q)rnd[q+0 1 2;3;`up] 
"9.639" "10.639" "11.639" 
q)rnd[q+0 1 2;3;`nr] 
"9.639" "10.639" "11.639"

 

Now: can we eliminate repetition? The up and dn functions differ by only a single keyword. And without delegating one of the modes to .Q.f?

(Hint: a single expression rnd is possible, ?80 characters.)

Eliminating up and dn is a good move, but that doesnt quite work.

 

q)rnd:{(`up`dn`nr!(f ceiling;(f:{string(x z*s)%s:10 xexp y})floor;.Q.f'))[z][;y;x]} 
q)q:9.638554216867471 
q)rnd[q;2;`up] 
{string(x z*s)%s:10 xexp y}[-_-:][;2;9.638554]

 

rnd:{[x;nd;m] string%[;s]((ceiling;floor;floor 0.5+)`up`dn`nr?m)@:x*s:10 xexp nd}

 

Here the case structure is not a dictionary, just a mapping from the symbols to functions. The primitives all iterate implicitly: the Each Left : is used only to support multiple rounding modes.

In the list of unary functions the third item floor 0.5+ is an implicit composition of two unaries: floor and the projection 0.5+.

up:{[x;nd]string%[;s]ceiling x*s:10 xexp nd}; 
dn:{[x;nd]string%[;s]floor x*s:10 xexp nd}; 
rnd:{[x;nd;m] d:`up`dn`nr!(up[;nd];dn[;nd];.Q.f[nd;]); (d m) each x};

Seems to work.

building on the above

rnd:{(`up`dn`nr!(f ceiling;(f:{string(x z*s)%s:10 xexp y})floor;.Q.f'))[z][;y;x]}

 

 

Spoiler
rnd:{[x;nd;m] string%[;s]((ceiling;floor;7h$)`up`dn`nr?m)@:x*s:10 xexp nd}?

Stealing your solution here, except floor 0.5+ can be replaced with 7h$. It is slightly more efficient and will save about 10 characters, if that matters