7 ms·
FizzBuzz in J, Explained
- throwaway7645 10y agoI really like the concepts in APL/J. It's probably why I'm so excited to see more array oriented support in Perl6. Keep up the postings.
- protomyth 10y agoIf you enjoy APL/J then you might want to check out F-Script on OS X. It was pretty fun for combining arrays with Smalltalk like syntax and might provide you with some amusement.
- empath75 10y ago'explained' I read it twice and still don't understand what it's doing.
- wyc 10y agoAuthor here. Sorry to hear that. If you want to let me know where I lost you via email or here, I'd be happy to revise it to be clearer. 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!
- acqq 10y agoConsider that on HN there are surely people who read about J the first time, or if they have read something somewhere, they already forgot it and they won't go through the whole tutorial (which is not small) before reading the article. 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.
- Avshalom 10y agoFB"0 basically means apply FB to each cell in an array. i.100 makes an array of numbers 0...99 and >: increments every number in that array by 1 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.
- acqq 10y agoThanks! So I made more than one bad guess. So >: can be monadic, increment and dyadic, bigger or equal. And i. can be a generator or an index of. 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?
- Avshalom 10y agoIn this case i. is monadic because the thing to the left (>:) is a verb(function). It can get ugly because J has weird precedence/associative rules to let it be tacit but this isn't one of those cases >: Is monadic because I guess " is special? And has a higher precedence than other things?
- avmich 10y ago" is conjunction, it binds to its arguments - in this case left FB and right 0 - stronger than those bind to their arguments. >: is verb, i. another verb, so >: 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.
- B1FF_PSUVM 10y agoJ makes my brain hurt, but in a much saner way than thisCamelCasedVariableNameIsNotNearlyLongEnoughToBeSufficientlyClear...WhereWasIGoing? So, props.
- 101km 10y ago> 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|])
- caioariede 10y agoI think comparing it with other languages may make it easier to grasp. Sometimes it's good to test it in a language you are used to. The selector expression could be represented in Python like this: a = lambda n: [n % x for x in [15, 3, 5]].index(0)
- 101km 10y agoYep, I started writing this out (first in js, but it doesn't map well, python list comprehensions do) but you beat me to it. :) 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.
- caioariede 10y agoIn fact, it's still not the same. In Python, the following expression will raise a ValueError. > a(2) In J, it will return the length of the array, as stated in the post.
- bkase 10y agoI found your post very compelling. I have tried messing around with the APL derivatives (J and K and Q), but never created anything substantial. I didn't know you could view the AST like that, and it did really help in your explanation. I'm definitely going to check out an APL again! Thank you for the great post!
- RodgerTheGreat 10y agoParaphrasing an earlier Reddit post, here's how it might look in K3. 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")
- Avshalom 10y agoTo translate that as literally as possible into Dyalog 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)∘.|⍵)))]}¨⍳100
- jxy 10y agoSince you are using ¨, ⍵ is a scalar, so you don't need the ∘. Translated 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.
- Avshalom 10y agoFair enough. Like most APL I wrote it backwards until it worked.
- lokedhs 10y agoWorks out-of-the box in GNU APL as well, if someone wants to try it with a free version of APL.
- bloaf 10y agoI have had several opportunities to write Mathematica code in a style similar to this. Specifically, building up functions to map over entire lists rather than iterating over them. 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.
- chx 10y ago:('Equilateral';'Isoceles';'Irregular';'Impossible'){::~3&,@<:@#@~.{~2&*@(>./)<+/ Belongs in https://www.stilldrinking.org/programming-sucks 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.
- klibertp 10y agoWhat you say is true for most languages, however, it's different for J. The sigils you see are the whole language - there are only around 20 sigils, which are the core functions of the language. You only need to learn and remember them, you can then work your way through any kind of J code. It may go slowly at the beginning, but as you get used to the most often occurring ways of composing them you get faster. 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.
- kaoD 10y agoI worked with J for some time (in my free time) and I pretty much had to learn it from scratch 3 or 4 times. And if I had to use J right now I'd had to learn again because I literally forgot everything. The syntax, the words, the verb trains... everything is downright absurd. 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?
- avmich 10y agoFrom http://www.jsoftware.com/help/jforc/culture_shock.htm#_Toc191734290 http://www.jsoftware.com/help/jforc/culture_shock.htm#_Toc19... , a great book about J: > 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.
- klibertp 10y agoThere's a blog post about FizzBuzzBazz (note the third value) in LiveScript, I posted a comment there, with J version[1], looking like this: 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 https://klibert.pl/posts/literate_j.html [1] http://livescript.net/blog/fizzbuzzbazz.html#comment-1145818066 http://livescript.net/blog/fizzbuzzbazz.html#comment-1145818...
- kazinator 10y agoThat cryptic code, ironically, doesn't appear significantly shorter, if at all, than the FizzBuzz solutions in some ordinary, readable languages. What's the point?
- alimw 10y agoIt's been a while since I used J, but as I recall it's more idiomatic (though sometimes trickier) to write a function that works directly with arrays than to write a function that works with scalars and then to map it over an array.
- bithive123 10y ago"Doesn’t it make sense that in mathematics, we use the integral symbol instead of A.Integral(B)?" 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.
- ninkendo 10y agoAgreed. And to me it's not just the math notation that bugs me, but the tendency of mathematicians (and perhaps by extension, physicists) to always use one-letter variables. In software development, you'd get torn apart in code review if your variables are all one letter long. Why is math so terse?
- evincarofautumn 10y agoI’m sure part of the reason is that mathematics was, and largely still is, written by hand. You can manipulate “code” more easily on paper with a terse, implicit notation. It can also have the advantage of engaging your visual pattern-matching abilities more than prose or prose-like notation.
- imh 10y agoI think it's pattern matching and intuition. If I see y = k/x, I can immediately process what that looks like. Further, I can take advantage of tons of conventions that make it read differently from a = b/c or even y = z/x. The conventions take some getting used to, but after a while writing out dependentVariable = divideBy(constant, independentVariable) would get super tedious and it's absolutely not instantly pattern matchable. Especially when you're working out some manipulation where you have to write the same variable a zillion times, and maybe even depending on your brain's pattern matching and inspiration to kick in. Hell, at this point I even use a shorthand for cos and sin for those reasons.
- rtpg 10y ago
- johnvaluk 10y agoIf the string 'FizzBuzz' appears in your code, you have failed FizzBuzz.
- taeric 10y agoI tried my hand at a terse fizz buzz, once. https://twitter.com/taeric/status/250060748548624385 https://twitter.com/taeric/status/250060748548624385 Not really hard to come up with, but does take effort. Would be difficult to go through that for all programs.