Now here’s a task that cries out for a simple solution: put a border round a matrix. My matrix is boolean and represents a QR code (yes, we’ll come to that) but it’s the same problem as, say, putting a 1px border on an image.
So let’s examine it as wrapping a char matrix in spaces. We’ll use 3 4#"ABCDEGHIJKL".
Our first strategy is to manipulate indexes, which is often an efficient approach in q. We make a larger blank matrix for the result and write the original matrix in the right place.
Start with the shape of the matrix; that is, count the rows and columns. (Shape is a concept that q did not inherit from its ancestor APL, but is easy enough to calculate.) We use the Zen monks for a point-free expression.
q)show M:3 4#"ABCDEFGHIJKL"
q)count each 1 first\M / shape of M
So the result shape is 5 6, and here is our blank template:
For the last move we could use Amend Each .' to map each item of M to a row-column pair in the result. But it should be more efficient to raze M and use Amend At@ to map all its items to the vector prd[n]#" " and then reshape it. Something like
Above, ??? is some expression that returns the target indices for the items of M. Let’s start with an easy expression – wrong, but easy. We’ll write the items of M into the first positions of the result.
Next we come to an often-overlooked overload of vs and sv: they encode and decode different (and variable) arithmetic bases. English pounds have 100 pennies (once known as New Pence) but once had 240, of which 12 made a shilling; and 20 shillings a pound.
q)240*4.50 / £4.50 in old pence
q)100 20 12 vs 240*4.50 / £4.50 was £4 10s 0d.
4 10 0f
q)%[;240]100 20 12 sv 4 10 0 / £4/10/- in decimal coinage
q)%[;240]100 20 12 sv 4 17 6 / not every sterling amount has an exact equivalent
We can use vs and sv to convert between row-col pairs and equivalent vector indices.
The above has a certain elegance in that it is probably efficient for a large matrix, but it does seem a lot of code for a simple task. If our matrices are small, perhaps we can see a simpler way?
Join , looks like an obvious candidate. (And it will lead us to something about flip we might not have known; but we’ll come to that.) We have to apply it to each of four sides, but we have decided we don’t necessarily need the fastest expression for this.
Looks straightforward: Join for top and bottom, Join Each for the sides.
How does that work? It turns out that flip uses scalar extension. The items of its argument must conform; that is, they must be same-length lists – or atoms. But the result will have same-length lists.