@Adám not particularly. Maybe take a few challenges you consider interesting to solve in APL? Even if it already has an answer in APL, we might get something different out of it or something?
@ngn ⍺ is a vector of 7s and 9s. ⍵ is an integer vector. If I encode using ∊{(⍺=9),(⍺/2)⊤⍵}¨ how would you decode? I.e. the first bit indicates whether the next integer uses 7 or 9 bits, then the bit after that indicates for the next int. Is it possible to decode without a loop?
How about when i'm dealing with arbitrary size sets. a parity bit starts first, if it's 0, the next 7 bits are a value, otherwise the next 9 bits are a value
So my idea is that we'll generate such data.
So we have some numbers, say 31 415 92 65 359 and some bit-widths, say 7 9 7 7 9.
And we want a single Boolean vector result. The first bit will be a 0 to indicate that the next seven bits form a number, then we have the binary representation of 31 and then a 1 to indicate that the next 9 bits represent the next number, then that number's binary representation, etc.
Let's take it step by step: First we encode 31 as 7-bit binary:
Let's talk about efficiency and doing things "the APL way".
While this expression is all short and clear and uses arrays, it really doesn't much array oriented computing. The ¨ just hides the fact that we're looping through the data.
Note that APL lists each bit-position as a row, so the first has all the highest bits of all the numbers. You often need to transpose after using ⊤ (or before using ⊥).
Also, note that when we have a 9-bit'er, we need an extra 1 on the far left (that is, as a new 10th high-bit).
At the same time, we know that 7-bit'ers have two leading 0s (because they'll never be higher than 127).
So if we add a 1 high-bit to all the representations, we just need to chop 2 bits from the 7-bit'ers. That'll remove the new high-bit and the 9th bit, leaving the 8th bit (a 0) as indicator.
Of course, we could use 1⍪ to put a high bit on top of the matrix, but the mathematical way would be adding 2*9 and then encoding everything in 10-bit binary:
We'll use this to replicate 1 to get a mask for the first two bits. Bit out data has 8 additional bits that we always want, so we need to insert 8s after each number:
(Also, btw if you ever find yourself using ⍨¨ you should replace it with ¨⍨ for performance. This is because then APL only needs to swap the arguments once instead of for each pair. If you think about it you'll realise that ⍨¨ and ¨⍨ always are equivalent.)
@all Any ideas on how to interleave 8s without a loop?
@Cowsquack Nice, but for the full performance, we can do even better by directly merging the two parts into a matrix rather than first converting the vector, and then concatenating:
,[1.5] concatenates along a new axis which is after the first (1), i.e. the first axis (elements of the vector) remains the first axis (rows of the matrix), while the new elements come after that as an additional 2nd axis (columns of the matrix).
Also note that I used , instead of ∊. This is because , is a simpler operation, so it is clearer to the reader that I'm just ravelling, not trying to flatten any nestedness.
First, let's generate some test data. This will be a little bit neater in ⎕IO←0, so we just have to remember to change 1.5 to 0.5 when we get that far.
We need some random bit-widths. Since we have two options (7 or 9), we will use ?2 to get a random bit. Then we can multiply that by 2 and add 7 to get 7 or 9: 7+2×?2
Now there is a really useful user command called ]RunTime which allows you to measure and compare the time it takes to execute code. It can even draw nice bar graphs to help you visualise the results.
So, the lesson from today is to avoid loops. See if you can express what you want using mathematical relationships, and if you need to insert of remove data, use /.
For 50 years, APL implementors have refined the algorithms to do array operations in the most efficient manner, and APL written in a fairly straight-forward manner can easily beat carefully hand-crafted C.
@H.PWiz Well, you generally want to fuse loops if possible, indeed, the interpreter may occasionally detect that your code has no side effects, and do so for you. I'd write it like you did too. We call things like ∊(w=9),¨n⊤¨⍨w⍴¨2too much pepper because all the ¨s looks like somebody sprinkled pepper on the expression.
Btw, in 16.0 and earlier, ⊃,/ was much faster than ∊ (when they are the same, that's not always), but in 17.0, ∊ is faster.