FizzBuzz in J, Explained
wycd.net
wycd.net
Generate a range of numbers up to 20 (for brevity), inclusive:
1+!20
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Apply a function ({x!/:3 5}), which takes an element modulo 3 and 5, to the range: {x!/:3 5}1+!20
(1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2
1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0)
Taking the negation (~) shows us the places the elements divide evenly: {~x!/:3 5}1+!20
(0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0
0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1)
Convert these into base-2 indices: {2 _sv~x!/:3 5}1+!20
0 0 2 0 1 2 0 0 2 1 0 2 0 0 3 0 0 2 0 1
We can then use those indices to select from a list. Note the addition of a map ('), because if we continue performing all these operations in parallel we won't have access to x as some particular item of the list: {(x;"Buzz";"Fizz";"FizzBuzz")2 _sv~x!/:3 5}'1+!20
(1;2;"Fizz";4;"Buzz";"Fizz";7;8;"Fizz";"Buzz";11;"Fizz";13;14;"FizzBuzz";16;17;"Fizz";19;"Buzz") fizzbuzz ← {
{(⍵ 'buzz' 'fizz' 'fizzbuzz')[1+2⊥0=3 5∘.|⍵]}¨⍵
}
called as fizzbuzz ⍳100
or just {(⍵ 'buzz' 'fizz' 'fizzbuzz')[1+2⊥0=3 5∘.|⍵]}¨⍳100
with unnecessary parens {(⍵ 'buzz' 'fizz' 'fizzbuzz')[1+(2⊥(0=((3 5)∘.|⍵)))]}¨⍳100Translated back to J
((;'Buzz';'Fizz';'FizzBuzz'"_){~2#.0=3 5|])"0>:i.100
The boxed list in J feels a bit cumbersome compared to mixed arrays in APL. {(x;"Buzz";"Fizz";"FizzBuzz")@2 2/~3 5!\:x}'1+!20
k6 swapped the argument order of "mod" and made change of base into an overload of "/", rather than a named builtin. See it in action here:http://johnearnest.github.io/ok/index.html?run=%20%7B%28x%3B...
What about something like
n{:[~#y;$x;y]}'{,/("Fizz";"Buzz")@&~x!/:3 5}'n:1+!20
to avoid duplicating the string combinations? The conditional is unfortunate, would like to get rid of it. But should be straightforward to extend if we want to introduce further string combinations for numbers divisible by, e.g., 7. Though anticipating such things goes against the spirit of k I suppose. :-)In my experience the obscure notation is what made college math hard for me to learn. As a programmer I really wish there was a math textbook that used A.Integral(B) type notation to teach concepts, because I think I could have been good at math.
I realize that people who start with math have the opposite experience; my physicist friend complains that whenever he has to use a CLI or write code everything seems arbitrary and confusing to him.
In software development, you'd get torn apart in code review if your variables are all one letter long. Why is math so terse?
What name do you ascribe to "a continuous function from R to R"?
Another detail is that you are rarely juggling more than 10 or so names at once.
A lot of math at a higher level becomes abstract enough to where elements no longer have a concrete meaning. What does the x in integral(f(x)dx) mean? It's more about some things being harder to write in point free style rather than anything else.
This is perhaps why it's common to have short names for arguments in functional languages as well.
Each step feels very intuitive and satisfying. But when you just look at the end result of any non-trivial example, it nearly always looks unreadable. I found myself needing to work quite hard to understand functions I myself had written, even if I was only interrupted by lunch.
> 1{::(('FizzBuzz';'Fizz';'Buzz';":) 1337)
Fizz
Is equivalent to: > ['FizzBuzz', 'Fizz', 'Buzz', 1337][1]
Fizz
In a language with less foreign syntax.
You can make it more familiar by putting the index on the right side of the array with '~': (('FizzBuzz';'Fizz';'Buzz';":) 1337){::~1
Fizz
This is the modulo bit: (0 i.~15 3 5|]) a = lambda n: [n % x for x in [15, 3, 5]].index(0)Now great, you can get the weird syntax and unroll it if you squint. Why is it written like this? Isn't it wasteful to test against all three cases (15,3,5) instead of returning early?
Probably has something to do with 'thinking in arrays' and how its a cheap comparison and that there's probably matrix multiplication or bit twiddling going on under there. That's the interesting bit to me.
To get the feel for an array language. This fizzbuzz example is actually surprisingly interesting because of that.
> a(2)
In J, it will return the length of the array, as stated in the post.I think it takes a certain skill to overcome the curse of already knowing something and then explaining it, and I'll definitely have some blind spots.
Thanks for reading it twice!
For example, I guess that the
FB"0 >:i.100
is a kind-of for loop, even if I also guess it's not called as such in J, but I wouldn't be able to explain how it exactly works, except that >: is bigger or equal, which I glimpsed from some first page of the tutorial, and i. should mean index of, according to your text? But searching 100 in what? Or is it some new word?
Then, why was 1337 introduced in the explanation at all? I failed to understand, reading the text not too slowly.
Thanks for posting the article here, it's refreshing.
So it's mapping FB over the numbers 1 to 100.
Now I don't know j very well so I don't know why this evaluates as (FB"0) (>:i.100) instead of (FB") (0>:i.100) but I guess " is special
1337 is just a stand in number. To try the algorithm without mapping it to an array.
So the algorithm is this make an array of [fizzbuzz fizz buzz x]. Then make an array of x mod 15, 3, 5. Find the first instance of 0 in that second array and use that index to select fizz,buzz etc. If there isn't a zero (0 i. secondarray) returns the length of secondarray which is 3, the same index as x in the first array.
But how to avoid reading the wrong meaning? If I'm reading right to left, I read 100, then I see i. then see something on the left of i. then why is it not a dyadic i. ? Because there is no parenthesis?
>: Is monadic because I guess " is special? And has a higher precedence than other things?
>: i. 100
is understood as
>: (i. (100))
- as two monadic (one-argument) functions applied sequentially.
To the left of that application sits the next verb -
FB"0
which applied next, so the whole thing is
FB"0 (>: (i. (100)))
Precedence/associative rules are: all verbs from right to left, all adverbs/conjunctions from left to right, adverbs/conjunctions are higher by precedence than verbs. That's it.
Until you get to hooks and forks.
The only thing other than "practice" you can do is to use built-in J expression pretty-printer. It shows you a graph of an expression, grouping verbs with their arguments. I don't remember how to get to it exactly, I think it was somewhere in foreigns.
EDIT: it's even mentioned in the article, don't know how I missed it: 5!:4
5!:4 <'FB'
I see here:
http://www.jsoftware.com/help/dictionary/dx005.htm
That every combination of two numbers means something special.
http://www.jsoftware.com/help/dictionary/dx018.htm
Wow.
So, props.
fu=:(3&|),(5&|),:(7&|)
g1=:(,&'zz')"1>;:'Fi Bu Ba'
((0=fu i.101){"1(3 1$<''),.(,.<"1 g1)),<"0 i.101
Well, what else can I say? J is really fun to play with.It's also not true that it cannot be maintainable or readable. J crazy parsing rules and other language features make it very flexible, on the level of Lisp, TCL, PERL, Smalltalk or Io. This means it can be a completely unreadable mess, as well as a readable and maintainable code, depending on who writes it. I made an attempt at writing readable - articulate - J: https://klibert.pl/posts/literate_j.html
[1] http://livescript.net/blog/fizzbuzzbazz.html#comment-1145818...
Not really hard to come up with, but does take effort. Would be difficult to go through that for all programs.
Belongs in https://www.stilldrinking.org/programming-sucks
In other words: I would not want to work with a language like this because I know most of the work is maintenance and it's hell if you need to work with something this sigil heavy.
In other words: you can't really say anything about how it feels to work with J without working with it for some time. This may be true to some extent with other languages, but J is unique/different enough to make this point especially true.
I really wanted to like it so I could program in my phone, but it's just too absurd to work for me.
The basic idea I got from experienced programmers was: you don't code like that in the real world. Real-life code should look a lot more like code in other languages, with no tacit programming and a lot of redefining of one-char words into actual words to improve readability. Then... why would I code in J at all? And why am I not taught to code like that in J's help?
> I've looked at some J code. Every other character is a period or a colon. I've got spots before my eyes. How can anybody read this stuff?
> You'll get used to it. J has a great many primitives, and it's important to keep the names short so that you can fit a lot of computation on one line; so the names are either single characters, like >, or a character with a period or colon appended (>. and >:). The period/colon is just part of the name. Single letters with period/colon, like i., are also used for primitives. If you want to assign your own names to the primitives, you are allowed to, but pretty soon you'll want to go back to the shorter names to save space and typing.
What autocomplete you need in J? For verbs and conjunctions which are at most 2 letters long?
> how do you give ideas whether the user should use @ or ^ at a given point?
In the same way as IDE helps you to choose . or exp() ? They are completely different concepts, how can you mix them?
> it's near impossible to search for code examples and such
I think it's a common problem for complex code examples, in any language. You may, though, search for comments.
From http://www.jsoftware.com/help/jforc/foreword.htm#_Toc1917342... :
> C is a computer language; it lets you control the things the computer does. J is a language of computation: it lets you describe what needs to be done without getting bogged down in details
I think it's a great help. J doesn't just reduces typing; it also reduces the amount of things one needs to keep in the head to solve the problem.
I even coerced SES (the Emacs spreadsheet) to act as an editor for large arrays.
I would argue that I have suceeded at least partially, such that I can say that it's definitely possible to implement an IDE for APL.
But, as you say, browsing through a large codebase in J would be not be a fun experience. The only way to handle it for me would be to comment it heavily and keep each line as short as possible (which is indeed what maintainable J files tend to look like). So I reserve it for experimentation - and I'm finding it very adapted for the purpose.
I doubt you'll need a lot of maintenance for short (and thought-out) solutions.