« first day (462 days earlier)      last day (2195 days later) » 

12:00 PM
the dyalog knowledge base is a treasure trove http://dfns.dyalog.com/n_decay.htm
 
 
1 hour later…
1:29 PM
@J.Sallé Any ideas for another APL lesson?
 
@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?
 
 
1 hour later…
ngn
2:50 PM
@Adám perhaps a lesson on J or k (not me though) or I (if you can get the author here to chat)?
@Feeds gosh... that avatar is creepy
 
@ngn Yeah, probably not going to happen.
@ngn I do have an idea though.
 
3:30 PM
@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?
 
ngn
@Adám define "loop" :)
 
 
2 hours later…
5:09 PM
Adám has added an event to this room's schedule.
 
5:30 PM
Welcome to APL Cultivation, inspired by:
in The Nineteenth Byte, 19 hours ago, by moonheart08
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:
⍞←31⊤⍨7⍴2
 
@Adám 0 0 1 1 1 1 1
 
And then we prepend a 0:
⍞←0,31⊤⍨7⍴2
 
@Adám 0 0 0 1 1 1 1 1
 
Same for 9-bit encoding of 415 (except we prepend a 1 to indicate 9 bits):
⍞←1,415⊤⍨9⍴2
 
@Adám 1 1 1 0 0 1 1 1 1 1
 
5:35 PM
Now we can make this into a function:
⎕←7 {⍵⊤⍨⍺⍴2} 31 ⋄ ⎕←9{⍵⊤⍨⍺⍴2} 415
 
@Adám
0 0 1 1 1 1 1
1 1 0 0 1 1 1 1 1
 
OK, looks good, so let's just apply it to each pair:
⎕←7 9 7 7 9 {(⍺=9),⍵⊤⍨⍺⍴2}¨ 31 415 92 65 359
 
@Adám
┌───────────────┬───────────────────┬───────────────┬───────────────┬───────────────────┐
│0 0 0 1 1 1 1 1│1 1 1 0 0 1 1 1 1 1│0 1 0 1 1 1 0 0│0 1 0 0 0 0 0 1│1 1 0 1 1 0 0 1 1 1│
└───────────────┴───────────────────┴───────────────┴───────────────┴───────────────────┘
 
Notice that I added in (⍺=9), to insert the bit-width indicator.
So then we just need to flatten the result:
⍞←∊7 9 7 7 9 {(⍺=9),⍵⊤⍨⍺⍴2}¨ 31 415 92 65 359
 
@Adám 0 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 0 1 0 1 1 1 0 0 0 1 0 0 0 0 0 1 1 1 0 1 1 0 0 1 1 1
 
5:38 PM
And that's our result.
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.
 
@Adám I got confused for a second there because I thought it was an assignment >.> I've been doing too much Java lately
 
Now remember that APL is a mathematical notation, which just happens to fit on a normal line and is machine executable.
So let's see if we can attack this problem in a more mathematical way.
 
@DyalogAPL @Adám just to clarify, this whole vector is each number encoded? So we know to look for the next 7 bits if the first is 0, and 9 otherwise?
 
@J.Sallé Yes, exactly.
 
Okay, then we're clear.
 
5:44 PM
Using 7 or 9 bits gives the same result, except for those numbers that need 7, 9 will give them two unneeded leading bits.
So if we encode all the numbers as 9-bit, and then chop the first two bits of the 7-bit'ers, we get the same result.
⎕←(10⍴2)⊤31 415 92 65 359
 
@Adám
0 0 0 0 0
0 1 0 0 1
0 1 0 0 0
0 0 1 1 1
0 0 0 0 1
1 1 1 0 0
1 1 1 0 0
1 1 1 0 1
1 1 0 0 1
1 1 0 1 1
 
ngn
is "train\" a considered a loop?
 
@ngn I guess, but it is still elegant.
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 ).
 
@Adám heh, ¨ is so un-APL-y :P
 
ngn
@EriktheOutgolfer the term is loopy :)
 
5:47 PM
Yes. You've been experimenting with "original" APL, where it didn't exist.
 
@ngn that's a more specific term...
 
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:
⎕←(10⍴2)⊤512+31 415 92 65 359
 
@Adám
1 1 1 1 1
0 1 0 0 1
0 1 0 0 0
0 0 1 1 1
0 0 0 0 1
1 1 1 0 0
1 1 1 0 0
1 1 1 0 1
1 1 0 0 1
1 1 0 1 1
 
Of course, at this point, we could split the columns apart and chop from each:
 
Hi, sorry, I missed the first part of this. What are we doing?
 
5:53 PM
@HyperNeutrino Go back and read. We're not so far yet.
⎕←(2×7 9 7 7 9=9)↓¨,⌿(10⍴2)⊤512+31 415 92 65 359
 
@Adám
┌───────────────────┬───────────────┬───────────────────┬───────────────────┬───────────────┐
│1 0 0 0 0 1 1 1 1 1│1 0 0 1 1 1 1 1│1 0 0 1 0 1 1 1 0 0│1 0 0 1 0 0 0 0 0 1│0 1 1 0 0 1 1 1│
└───────────────────┴───────────────┴───────────────────┴───────────────────┴───────────────┘
 
oh it just started, perfect. thanks
 
Oops, the other way:
⎕←(2×7 9 7 7 9=7)↓¨,⌿(10⍴2)⊤512+31 415 92 65 359
 
@Adám
┌───────────────┬───────────────────┬───────────────┬───────────────┬───────────────────┐
│0 0 0 1 1 1 1 1│1 1 1 0 0 1 1 1 1 1│0 1 0 1 1 1 0 0│0 1 0 0 0 0 0 1│1 1 0 1 1 0 0 1 1 1│
└───────────────┴───────────────────┴───────────────┴───────────────┴───────────────────┘
 
Now we flatten:
⎕←∊(2×7 9 7 7 9=7)↓¨,⌿(10⍴2)⊤512+31 415 92 65 359
 
5:54 PM
@Adám
0 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 0 1 0 1 1 1 0 0 0 1 0 0 0 0 0 1 1 1 0 1 1 0 0 1 1 1
 
But wait, we fell into the loopy trap again!
 
@Adám uh, excuse me, but I think that 512 is 2⁹, not 2¹⁰
 
That is what he said
 
@EriktheOutgolfer Yes. Did I say otherwise?
2*9 is a single 10th bit on, the rest are off.
 
@Adám ah sorry, got confused because I was studying while reading...
 
5:58 PM
@EriktheOutgolfer That's OK, I also made a mistake back there, using 10⍴ instead of 9⍴.
Anyway, let's see if we can remove all the unnecessary interspersed bits in one swoop, without looping.
Remember Replicate / ?
Replicate replicates (doh!):
⍞←3 1 4/10 20 30
 
@Adám 10 10 10 20 30 30 30 30
 
But it has a trick up its sleeve. If the replication number is negative, it replicates the prototypical element of the array instead:
⍞←3 1 ¯4/10 20 30
 
@Adám 10 10 10 20 0 0 0 0
 
So we can use this to generate a bit mask for the first two bits of each number.
For the 9'ers, we need 2 trues, and for the 7'ers, we need ¯2 trues, i.e. 2 falses.
First, we can get the right sign by subtracting 8:
⍞←7 9 7 7 9-8
 
@Adám ¯1 1 ¯1 ¯1 1
 
6:04 PM
Then we just need to multiply by 2:
⍞←2×7 9 7 7 9-8
 
@Adám ¯2 2 ¯2 ¯2 2
 
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:
⍞←∊8,⍨¨2×7 9 7 7 9-8
 
@Adám ¯2 8 2 8 ¯2 8 ¯2 8 2 8
 
No good. We're looping again!
(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?
 
⍞←∊8,⍨⍪2×7 9 7 7 9-8
 
6:09 PM
@Cowsquack ¯2 8 2 8 ¯2 8 ¯2 8 2 8
 
@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:
⍞←,8,[1.5]⍨2×7 9 7 7 9-8
 
@Adám ¯2 8 2 8 ¯2 8 ¯2 8 2 8
 
,[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.
 
@Adám wait what? We can do this to interleave any vector with a scalar?
 
@J.Sallé Yes, or more generally, any array with any array, inserting new axes before/between/after any existing axes.
⎕←1 2 3,[1.5]10 20 30
 
6:15 PM
@Adám
1 10
2 20
3 30
 
⎕←1 2 3,[0.5]10 20 30
 
@Adám
 1  2  3
10 20 30
 
@Adám that might have changed my life ⍨
 
@J.Sallé APL has been able to do this for over half a century (APL\360 from '66 can do it).
 
@Adám well, TIL >.>
 
6:18 PM
OK, so now we've got a replication vector, so we can get a bit mask by using that to replicate 1:
⍞←1/⍨,8,[1.5]⍨2×7 9 7 7 9-8
 
@Adám 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 
Here you can see that we're going to take out the first two bits, then leave the next 7 and the next 9, etc.
Now let's go back to our binary matrix. We need all the bits of the first number, then all the bits of the second, so we'll transpose and ravel:
⍞←,⍉(10⍴2)⊤512+31 415 92 65 359
 
@Adám 1 0 0 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 0 1 0 1 1 1 0 0 1 0 0 1 0 0 0 0 0 1 1 1 0 1 1 0 0 1 1 1
 
And putting it all together:
⍞←(1/⍨,8,[1.5]⍨2×7 9 7 7 9-8)/,⍉(10⍴2)⊤512+31 415 92 65 359
 
@Adám 0 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 0 1 0 1 1 1 0 0 0 1 0 0 0 0 0 1 1 1 0 1 1 0 0 1 1 1
 
6:21 PM
There you go. That's the APL way.
So is this really better? Let's find out.
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
Let's have 10 of those:
⍞←5+2×?10⍴2 ⊣ ⎕IO←0
 
@Adám 7 5 5 7 5 7 7 7 5 7
 
⍞←7+2×?10⍴2 ⊣ ⎕IO←0
 
@Adám 7 9 9 7 9 7 7 7 7 7
 
There. (Of course, we could use 7 9[?10⍴2] too, but math is more fun.)
Then we need some numbers. 7-bit'er may be up to ¯1+?2*7 (in ⎕IO←0) and so too for 9-bit'ers:
⎕←?2*⎕←7+2×?10⍴2
 
@Adám
9 11 11 9 11 11 9 11 9 11
383 1895 1786 183 614 1494 484 1662 103 1413
 
6:28 PM
Oops. I have to set ⎕IO each time :-)
⎕←?2*⎕←7+2×?10⍴2 ⊣ ⎕IO
 
@Adám
11 9 9 9 11 11 11 9 11 9
1963 120 69 448 1470 1478 288 281 1964 334
 
That looks good. We can assign it all together with n←?2*w←7+2×?10⍴2.
 
@Adám did you actually set it there?...
 
@EriktheOutgolfer No. Silly me. Maybe I should actually look before saying it looks good.
⎕←?2*⎕←7+2×?10⍴2 ⊣ ⎕IO←0
 
@Adám
9 9 7 7 7 7 7 9 7 7
362 485 69 103 116 64 64 210 120 1
 
6:30 PM
Finally.
⋄ n←?2*w←7+2×?10⍴2 ⋄ ⎕←(1/⍨,8,[.5]⍨2×w-8)/,⍉(10⍴2)⊤512+n
 
@Adám
LENGTH ERROR
 
⋄ ⎕IO←0 ⋄ n←?2*w←7+2×?10⍴2 ⋄ ⎕←(1/⍨,8,[.5]⍨2×w-8)/,⍉(10⍴2)⊤512+n
 
@Adám
1 0 0 0 0 1 1 0 1 1 0 1 1 0 1 0 0 0 0 1 0 1 0 1 1 1 0 0 1 0 0 0 1 0 0 1 0 1 1 0 0 1 0 0 1 0 1 0 0 1 0 1 1 1 0 0 1 1 0 0 1 1 0 1 0 0 1 1 0 1 1 1 0 0 1 1 0 0 0 0 1 0 1 0
 
Right.
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.
]RunTime ⍴⍴⍳9 ⍴∘⍴⍳9 -compare
 
@Adám

  ⍴⍴⍳9  → 1.4E¯7 |    0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
  ⍴∘⍴⍳9 → 3.4E¯7 | +152% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
 
6:35 PM
(⍴∘⍴ takes longer because the interpreter recognises ⍴⍴ and takes a shortcut.)
 
@DyalogAPL ⍴⍴ is an idiom...(ninja)
 
However, we need to call it after making the test data, so we'll have to call it through the user command processor ⎕SE.UCMD:
⎕←'Hello' ⋄ ⎕←⎕SE.UCMD 'runtime ⍴⍴⍳9 ⍴∘⍴⍳9 -c'
 
@Adám
Hello

  ⍴⍴⍳9  → 1.1E¯7 |    0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
  ⍴∘⍴⍳9 → 3.2E¯7 | +182% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
 
Now let's compare our original loopy solution to our non-loopy solution:
 
waits for beat drop
 
6:38 PM
n←?2*w←7+2×?10⍴2 ⊣ ⎕IO←0 ⋄ ⎕←⎕SE.UCMD 'runtime -c  (1/⍨,8,[.5]⍨2×w-8)/,⍉(10⍴2)⊤512+n  ∊(w=9),¨n⊤¨⍨w⍴¨2'
⋄ n←?2*w←7+2×?10⍴2 ⊣ ⎕IO←0 ⋄ ⎕←⎕SE.UCMD 'runtime -c  (1/⍨,8,[.5]⍨2×w-8)/,⍉(10⍴2)⊤512+n  ∊(w=9),¨n⊤¨⍨w⍴¨2'
 
@Adám

  (1/⍨,8,[.5]⍨2×w-8)/,⍉(10⍴2)⊤512+n → 2.6E¯6 |    0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
  ∊(w=9),¨n⊤¨⍨w⍴¨2                  → 7.7E¯6 | +198% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
 
Btw, ]RunTime will warn us with a star to the far left if any result is different, so no stars there means the two results are the same
Our loopy solution takes 3 times as long for only 10 numbers. How about for 100 numbers?
⋄ n←?2*w←7+2×?100⍴2 ⊣ ⎕IO←0 ⋄ ⎕←⎕SE.UCMD 'runtime -c  (1/⍨,8,[.5]⍨2×w-8)/,⍉(10⍴2)⊤512+n  ∊(w=9),¨n⊤¨⍨w⍴¨2'
 
@Adám

  (1/⍨,8,[.5]⍨2×w-8)/,⍉(10⍴2)⊤512+n → 7.1E¯6 |    0% ⎕⎕⎕⎕⎕
  ∊(w=9),¨n⊤¨⍨w⍴¨2                  → 5.8E¯5 | +724% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
 
Yeah, you can see where this is going…
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.
And that's all for this lesson.
 
@H.PWiz

  (1/⍨,8,[.5]⍨2×w-8)/,⍉(10⍴2)⊤512+n → 8.1E¯6 |    0% ⎕⎕⎕⎕⎕
  ∊(w=9),¨n⊤¨⍨w⍴¨2                  → 5.9E¯5 | +632% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
 
6:47 PM
⋄ n←?2*w←7+2×?100⍴2 ⊣ ⎕IO←0 ⋄ ⎕←⎕SE.UCMD 'runtime -c (1/⍨,8,[.5]⍨2×w-8)/,⍉(10⍴2)⊤512+n ∊w{(⍺=9),⍵⊤⍨⍺⍴2}¨n'
 
@H.PWiz

  (1/⍨,8,[.5]⍨2×w-8)/,⍉(10⍴2)⊤512+n → 7.2E¯6 |     0% ⎕⎕⎕
  ∊w{(⍺=9),⍵⊤⍨⍺⍴2}¨n                → 9.1E¯5 | +1169% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
 
Oh, I thought compressing into one each might have sped it up
 
ngn
@Adám "can easily beat carefully hand-crafted C" - hmmm...
let's just say, it might be able to beat not so well written C
 
@ngn Just wait for 17.0…
@H.PWiz Nah, you still have a loop, and it doesn't really matter so much if you loop each part separately or all of them together.
 
@Adám I remember reading somewhere that it was an optimisation. I can't remember where though
 
7:05 PM
@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⍴¨2 too 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.
 
Yes, I read that blog post
 

« first day (462 days earlier)      last day (2195 days later) »