Conversation started Apr 21, 2020 at 14:30.
Apr 21, 2020 14:30
Welcome to APL Cultivation!
Please excuse me if I'm a bit distracted, as I have a parallel meeting going on.
First thing: Today's subject.
One thing that has been suggested is how to write APL code for "do-while" type problems.
@all Any other ideas?
@AviF.S. just tested, the file i named definitely affects the Dyalog version of launching RIDE, can't really help other than try tracing where else could the configs be stored
RGS
RGS
@Adám I'm up for anything
@dzaima Thanks!
Do-while is a good idea; it's a good follow-on from last time's for-each.
OK. Maybe I don't have as much to say, but let's give it a go. Feel free to chime in with your own ideas.
Apr 21, 2020 14:33
@Adám Am curious in general if there are any other more APL-esque constructs for while/for structures without using the ∇WHILE-type constructs. They always struck me as not in the spirit...
Right, as Avi is mentioning, modern APL does have :While-:EndWhile and :Repeat-:Until.
I've often tried abusing the ⍣ operator, but as it's not meant for it, it usually doesn't work. Sometimes it does, but even then it's obscenely obfuscated
Yes, the operator is an option, you just need to know how to apply it right. We can look at that.
And then there's recursion, which isn't bad in APL, as you can use the optimised tail-recursion.
I dont mind the ⍣ if its 1/lambda, however (at least in gnu apl), the right tack still executes. For example:

⎕←'there'⊣⍣1⊢⎕←'here'
here
there
⎕←'there'⊣⍣0⊢⎕←'here'
here
here
(Perhaps also learning how to apply ⍣ with side-effects, eg, if you want to print particular values as you generate them)
Apr 21, 2020 14:37
/me still thinks in TradAPL, and will tend to write repeat-untils with an expression like →bool/linenumber at the bottom...
(So far, I usually initiate an empty list, and then append to it in ⍣ and then print it afterwards... perhaps there's a better way?)
@AviF.S. That's fine, if you're building a list. It could also be mutating a (semi-)global variable "until".
@cannadayr But that's a simple if/else. I'm not sure I understand what you mean. Can you explain more?
About , it is important to note that it always applies its left operand at least once.
So let's take a very simple (pun intended example). Lets say we have an array like ⊂⊂⊂⊂2 2⍴'ok'
We want to disclose it until it is simple. (If we do ⊃⍣≡ we'll end up with 'o'.)
(@dzaima Holy moly! I have to admit I have no idea what any of that is! Somehow, I managed to learn Vim and still don't know Bash!)
Apr 21, 2020 14:43
An easy pitfall is also to use in the right operand (the one that answers "are we done?") instead of .
      ⊃⍣{1≥|≡⍺} ⊂⊂⊂⊂2 2⍴'ok'
ok
ok
The problem is that our input might have 0 levels of nesting; then we fail:
      ⊃⍣{1≥|≡⍺} 2 2⍴'ok'
o
This is because is being applied once before we even ask if we're done.
So apply ⊂ once before you start disclosing?
That wouldn't be a general solution. There might not be an inverse.
If instead we move the test inside the left operand…
      {1≥|≡⍵:⍵ ⋄ ⊃⍵}⍣≡ 2 2⍴'ok'
ok
ok
RGS
RGS
So we're looking for a fixed point of the left operand?
Yes, because the left operand will become a no-op when we're done.
In fact, we can even use the power operator instead of the guard!
      {⊃⍣(1<|≡⍵)⊢⍵}⍣≡⊂⊂⊂⊂2 2⍴'ok'
ok
ok
      {⊃⍣(1<|≡⍵)⊢⍵}⍣≡ 2 2⍴'ok'
ok
ok
So is that basically {1≥|≡⍵:⍵ ⋄ ∇⊃⍵}?
without the ⍣
Apr 21, 2020 14:51
@xpqz Yours is the recursive equivalent, yes.
But the power version will be more efficient in Dyalog?
RGS
RGS
@Adám I missed the point of having a ⍣ inside the left operand; are guards frowned upon?
@RGS No, I'm just saying.
RGS
RGS
Ah ok, you were just showing us
Yep.
Of course, you don't have to write everything inline. You could use a separate function for the main processing.
In your left operand, you can of course place your done-condition at the top or at the bottom, or anywhere else.
But let's say instead that we don't want the condition to be based on the data processed. Rather, we want to periodically read an outside value to decide whether to continue or not.
You can try this in your local APL: done←0 ⋄ {⎕←⍵⊣⎕dl 5}⍣{done}&'work'
It will run in the background, printing "work" every 5 seconds.
RGS
RGS
Apr 21, 2020 15:02
Until "done" is set to 1
Yes, if you (quickly) enter done←1, it'll stop.
⎕dl is a delay function?
Yes. DeLay
/me nods
Of course, it didn't need to be a single value in {done}. It could be an entire function that figures out if we're done based on a bunch of stuff.
So, any remaining problems with ?
Apr 21, 2020 15:05
Wait, the solution given before of {⊃⍣(1<|≡⍵)⊢⍵}⍣≡⊂⊂⊂⊂2 2⍴'ok' was a general case fix because might not always have an inverse, you said. But in the end, it uses anyway...
I only just noticed
RGS
RGS
@AviF.S. We were always using ⊃, the function we really want to apply. someone suggested we started by using an extra ⊂
Not the other way around
@AviF.S. No, (i.e. the "work") might not have an inverse.
Haha, I'm sorry! I totally misunderstood :laughing emoji thingy:
I think this is what we need about . Shall we move on to recursion?
RGS
RGS
sure!
Apr 21, 2020 15:11
OK, so recursion can be done simply by calling the function name. Dfns can also call themselves using .
The benefit of is that you can rename the function or leave it anonymous.
RGS
RGS
Makes sense
I should also mention ∇∇.
If you are as advanced as writing your operators, you might want the operator's code to "use" itself. You do that with ∇∇.
Inside such a dop, you can also use as a shortcut for ⍺⍺∇∇ or ⍺⍺∇∇⍵⍵ depending on operator valence.
RGS
RGS
So with ∇∇ you can apply the operator to other arguments and ∇ acts as an alias for ⍺⍺∇∇ (or ⍺⍺∇∇⍵⍵)?
@RGS Exactly. ("operands" though)
RGS
RGS
@Adám ("operands" instead of "arguments", correct?)
Apr 21, 2020 15:18
Well, both, actually. allows you to apply to any desired arguments, but keeping the operands. ∇∇ allows you to also switch operands.
RGS
RGS
Understood!
Other than this, it is actually much the same as with : Establish the stop condition with a guard (or a control structure in a tradfn), and do the work otherwise.
I think a great example popped up…
in The Nineteenth Byte, yesterday, by Anush
CMC Write down 7 consecutive numbers so that the digit 2 is used exactly 16 times.
I answered with a recursive solution:
in The Nineteenth Byte, yesterday, by Adám
Found with {16=+/'2'=⍕⍵:⍵⋄∇1+⍵}⍳7 in APL. Try it!
While dzaima posted the -based alternative: 1∘+⍣{16=+/'2'=⍕⍺}⍳7
Notice here how the left operand (the "work") is extremely simple; just the increment function, while the actual work is in the condition function.
Very elegant answers
Yes, tail calls.
The important thing is that APL detects when the final result will be used unmodified as the result of the previous iteration.
So, let's say we wanted the beginning number of the 7-long sequence: {16=+/'2'=⍕⍵:⍵⋄⊃∇1+⍵}⍳7
Now APL has to keep track of where came from so we can apply that final .
Can we detect a tail call? Yes. :-)
You can try this: {⎕←≢⎕SI ⋄ 16=+/'2'=⍕⍵:⍵ ⋄ ∇ 1+⍵}2000+⍳7 (I've made it start searching at 2000 to prevent output flooding.)
⎕SI is the State Indicator, or stack.
So, every time around the loop, we count the frames on the stack and print that.
It'll print 1 every time, because the stack "forgets" about the previous call every time.
Now try it with the then: {⎕←≢⎕SI ⋄ 16=+/'2'=⍕⍵:⍵ ⋄ ⊃ ∇ 1+⍵}2000+⍳7
RGS
RGS
Apr 21, 2020 15:37
SI increases, but moving the ⊃ to the guard return makes it output only 1s again...
And yet, I don't understand what is the difference that allowed Dyalog to do that.
@RGS What is your code?
RGS
RGS
{⎕←≢⎕SI ⋄ 16=+/'2'=⍕⍵:⊃⍵ ⋄ ∇ 1+⍵}2000+⍳7
That one only prints 1s
I moved ⊃, of course... but I can't comprehend why Dyalog manages to optimize one and not the other
@RGS Of course, as that is the final result that gets passed back.
ngn
ngn
@Adám no need for there
@ngn True, not in Dyalog APL.
@RGS Dyalog looks ahead if the result will be used. This is also in effect for "shy" functions and assignments. Sometimes it will not even compute something if the result won't be used.
Apr 21, 2020 15:40
That means it also has to detect whether the computation has side effects.
@JeffZeitlin It doesn't take it that far. An APL interpreter has to anyway look a few tokens left to parse correctly.
ngn
ngn
@AviF.S. should be under ~/.config/Ride-4.3/ or similar. you can find out the exact path by pressing f12 and typing D.el.app.getPath('userData') in the js console
@ngn i don't like using of
@ngn ah, that's much more sane than straceing.. ¯\_(ツ)_/¯
@ngn Thanks a bunch! I'm trying it
@all Any questions about recursion?
RGS
RGS
Apr 21, 2020 15:46
@Adám (not from me)
Me neither! Thanks @Adám for doing this even despite the meeting
No problem. Let's finish with an exercise: I assume you're all familiar with the Fibonacci sequence. Try implementing Fib n (which returns the nth Fibonacci number) using and recursion.
RGS
RGS
the n-th term, the first n terms, or whatever we please»
?
> which returns the nth Fibonacci number
But by all mean, try "first n" too.
RGS
RGS
fib ← {0=⍵:⍺ ⋄ ⍺←1 ⋄ ⍵×⍺ ∇ ⍵-1}
Apr 21, 2020 15:51
@RGS I think you mean +, not ×
RGS
RGS
wait I did factorial ahaha
fib ← {0=⍵:⊃⍺ ⋄ ⍺ ← 0 1 ⋄ (¯2↑⍺,+/⍺) ∇ ⍵-1}
And checking with fib ← {⎕←≢⎕SI ⋄ 0=⍵:⊃⍺ ⋄ ⍺ ← 0 1 ⋄ (¯2↑⍺,+/⍺) ∇ ⍵-1} it uses tail recursion
Very nice. Well done!
RGS
RGS
If I wanted to recurse on a function that takes a left and a right argument, how could I do this "trick" of carrying the calculations down the recursion..?
@RGS You already did, no? There's a hidden "seed" argument 0 1
RGS
RGS
but that is the initialization, because I only really call fib with 1 argument
but say I wanted to call a function f with two arguments
Apr 21, 2020 15:59
Right, but say you'd implemented a Lucas number function instead.
RGS
RGS
and I would need a 3rd one for the actual calculations, going down the recursions...
I suppose I did the first n instead, whoops! This is just to test the bot, I've never tried it before
RGS
RGS
@Adám just let me google that real quick
{10=≢⍵:⍵ ⋄ ∇ ⍵,+/¯2↑⍵}1 1
Darn
How does one prompt it?
The bot died of COVID-19.
Apr 21, 2020 15:59
{⍵,+/¯2↑⍵}⍣{10=≢⍵}1 1
Haha, only fair!
ngn
ngn
@RGS make it an operator and use ⍺⍺, or put the args&accumulator in a vector
RGS
RGS
@Adám for the Lucas function I would only have to swap the init values of ⍺, right?
Is there a better way to do that, aside from the closed form, which sort of ruins the recursive point? And aside from the matrix version, which is more APL-esque but less... well, maybe I'm just being lazy
@RGS You'd simply not initialise inside, but let that be the left argument.
RGS
RGS
@Adám But that is because you took the example to be the Lucas numbers, which follow the same "add the previous two" pattern of Fibonacci and you abstracted away the seed
Apr 21, 2020 16:03
@RGS Right. So in a more general case, ngn's advice stands. Alternatively, you could use a variable outside to keep information in.
I think this concludes today's lesson. Thank you for participating!
RGS
RGS
@Adám yup, I see. It is just that ngn's advice will have me parse the input to discern if I'm already recursing or if I should start recursing now
 
Conversation ended Apr 21, 2020 at 16:04.