πΎ Archived View for l-3.space βΊ log-240222-1.gmi captured on 2024-05-26 at 14:22:26. Gemini links have been rewritten to link to archived content
β¬ οΈ Previous capture (2024-03-21)
-=-=-=-=-=-=-
Time-stamp: <2024-02-22 21h26 UTC>
Let's open boldly, this entire post is about the following
(β¨`' 'βΈβ )βΈ/βΒ»ββ₯(ββΏ3βΈβ₯(Β―3βΈΓβ)β(Γ·β3β )βΈβββ’)βΎΛβ£
or as CBQN's `)explain` helpfully puts it
(β¨`' 'βΈβ )βΈ/βΒ»ββ₯(ββΏ3βΈβ₯(Β―3βΈΓβ)β(Γ·β3β )βΈβββ’)βΎΛβ£ β β β β β β β β β β ββ β ββ β β β β β β β β β β β β β β ββ β ββ β β βΎΛβ β β β β β β β β β β ββ Γ·β3β β β ββ β β β β β β β β β β ββ βββ β β ββ β β β β β β β β β Β―3βΈΓβ β β β ββ β β β β β β β β β βββ β β β ββ β β β β β β β β β ββββββββ β β ββ β β β β β β β β β ββββββββΈβ β ββ β β β β β β β β β ββββ’ ββ β β β β β β ββ3 β β ββ β β β β β β ββββΈβ₯ β ββ β β β β β β βββββββββββββββββββ ββ β β β β β β βββββββββββββββββββββββΌβ£ β ' 'βΈβ β β β β β¨` β β β β β βββββ β β β β ββββββββΈ/ β β β βββΒ» β β ββββ₯ β βββββββββββββββββββββββββββββ βΆββββββββββββββ
Yep, it sure would. Instead of that, however, let's try an example
F β (β¨`' 'βΈβ )βΈ/βΒ»ββ₯(ββΏ3βΈβ₯(Β―3βΈΓβ)β(Γ·β3β )βΈβββ’)βΎΛβ£ (β¨`' 'βΈβ )βΈ/βΒ»ββ₯(β¨ β 3 β©βΈβ₯(Β―3βΈΓβ)β(Γ·β3β )βΈβββ’)βΎΛβ£ ',' FΒ¨ β¨"1", "10", "100", "1000", "10000", "100000", "1000000"β© β¨ "1" "10" "100" "1,000" "10,000" "100,000" "1,000,000" β©
(indented line is input, un-indentend line is output)
What's going on in the above? First we assign this function to a name so that it's easier to call it. Then we apply it. How? We have the two inputs, ',' on the left and the list on the right, the function in the middle and each (Β¨) modifier indicating we want to apply the function to each member of the list.
Thus you might well guess that using the embedded threes and the ',', this function groups 'digits' into groups of three separated by the provided character. Well ... that's exactly it.
I'm going to take various pieces of BQN for granted, but try to remind us of the larger structural parts as we go along. The first most important part is trains, see eg
2024-02-12 - APL-likes as a notation for thought
for an incompetent account. Recall from thereΒΉ that a 3-train is a composition
f g h
which when applied dyadically, that is to two arguments, yields the arguably intuitive result
π¨ (f g h) π© <===> g(f(π¨, π©), h(π¨, π©))
BQN (and other languages) offer up an extension of this 3-train parsing rule: a 3-train may be preceded by a monadic function m and the resulting semantics are
m f g h <===> m (f g h) Β² π¨ (m f g h) π© <===> m(g(f(π¨, π©), h(π¨, π©)))
With this in mind, let's see whether we can descry the broad outlines of what is going on above.
Let's trim-down the picture at the top of this page for a second
(β¨`' 'βΈβ )βΈ/βΒ»ββ₯(ββΏ3βΈβ₯(Β―3βΈΓβ)β(Γ·β3β )βΈβββ’)βΎΛβ£ β β β β βΎΛβ β ββ β βββββββββββββββββββββββΌβ£ β β ββ₯ β βββββββββββββββββββββββββββββ
I've purposefully suppressed some detail to emphasise the important features:
Thus the overall form of our function is
m f g h
or in greater detail
m <===> (...ββ₯) f <===> (...) g <===> βΎΛ h <===> β£ m f g h <===> (...ββ₯) (...) βΎΛ β£
The remaining parts of interest are the leading monadic function and the the left dyadic function in the 3-train. We'll take a quick look at those in what follows, but here, it's dangerous to go alone
known to its friends as
ββΏ3βΈβ₯(Β―3βΈΓβ)β(Γ·β3β )βΈβββ’ β β β β ββ β ββ β β β β β β ββ Γ·β3β β β β β β β ββ βββ β β β β β Β―3βΈΓβ β β β β β β βββ β β β β β β ββββββββ β β β β β ββββββββΈβ β β β β ββββ’ ββ3 β β ββββΈβ₯ β βββββββββββββββββββ βΆββββ
Here our outermost structure is that of a 2-train. Recall that we may combine a monadic function m with a dyadic function f as `m f` with application semantics
π¨ (m f) π© <===> m(f(π¨,π©))
In the above our monadic function m is
ββΏ3βΈβ₯
which reshapes the list it's applied to into Nx3 (where N, or 'β' in the code above, is determined dynamically based on the length of the list). Not too much going on over there.
On the other hand the dyadic function is somewhat more worth looking at.
(Β―3βΈΓβ)β(Γ·β3β )βΈβββ’
has two important things going on:
Here's the interesting part about about that monadic application: it's really a dyadic function being applied!
The crucial point here are the semantics of before (βΈ):
fβΈg π© <===> g(f(π©), π©) π¨ fβΈg π© <===> g(f(π¨), π©)
Isn't that so cool!? Incidentally the dual of before is after (β):
gβf π© <===> g(π©, f(π©)) π¨ gβf π© <===> g(π¨, f(π©))
and the fun observation is that using both before and after coincides with 3-trains (almost)
fβΈgβh π© <===> g(f(π©),h(π©)) <===> (f g h) π© π¨ fβΈgβh π© <===> g(f(π¨),h(π©)) <===> π¨ ((fβ£) g (hβ’)) π© Β³
Nice!
Putting this together, prefixes (β) is dyadic but we combine it with before of
(Β―3βΈΓβ)β(Γ·β3β )
which computes -3*ceiling(len( input )/3). What does taking a prefix of negative length do? Why, fill the beginning with the fill element
Β―20β"of course" " of course"
This is really the heart of the algorithm, after this we reshape the string-which-has-been-prefixed-padded-to-a-length-of-a-multiple-of-three to be a table of three-length strings, and fiddle with the table by joining on each row (βΎΛ) the separator (β£).
ββΏ3βΈβ₯(Β―3βΈΓβ)β(Γ·β3β )βΈβ "1000" ββ β΅" 1 000" β ',' ((ββΏ3βΈβ₯(Β―3βΈΓβ)β(Γ·β3β )βΈβββ’)βΎΛβ£) "1000" ββ β΅" 1, 000," β ',' (β₯(ββΏ3βΈβ₯(Β―3βΈΓβ)β(Γ·β3β )βΈβββ’)βΎΛβ£) "1000" " 1,000,"
... unfortunately this leaves us with an ugly prefix of blanks, as well as a trailing delimiter, which brings us to
(β¨`' 'βΈβ )βΈ/βΒ»ββ₯
There's not a whole lot going on here, except once again for this story about after (βΈ) so that
(β¨`' 'βΈβ )βΈ/ π© <===> (β¨`' 'βΈβ π©) / π© <===> (β¨`' 'β π©) / π©
Note: don't be caught out by ' 'βΈβ , ' ' is not a function. In this case βΈ is called bind
https://mlochbaum.github.io/BQN/doc/hook.html#bind
I'm going to stop short of explaining the outstanding details, hopefully with the excellent documentation and some interest the rest can be understood readily. To me what turned this mundane exercise in programming (group digits interspersing separator) into something interesting and worthwhile was first and foremost the lure of leveraging a useful implicit composition semantics with a robust set of combinators to express a functional solution. That we might express our thoughts in terms of arrays was an added and pleasant bonus.
All in all, i do hope that this short post manage to rouse some interestβ΄ in you dear reader and that your BQN (or APL-like) journey begins soon!
---
β° a soon-to-be-explained pun
ΒΉ or better yet, elsewhere
Β² the language is right associative
Β³ the parentheses around hβ’ are redundant
β΄ i'll settle for perverse curiosity