cases = (
(3, 'Fizz'),
(5, 'Buzz'),
(7, 'Bazz'),
(11, 'Boo'),
(13, 'Blip'),
)
for i in range(1, 101):
out = []
for c in cases:
if i % c[0] == 0:
out.append(c[1])
if out:
print ''.join(out)
else:
print i
Edit: not to detract from the post's point, I think it's valid. Monoids are cool and all but simple counting arguments can take you a long, long, long, way when case analysis fails you.
This can be edited down to six lines with a simple trick: a Python string multiplied by True will return itself, and multiplied by False will return the empty string.
for i in range(110):
pr = ""
pr += "Fizz" * (i%3 == 0)
pr += "Buzz" * (i%5 == 0)
pr += "Bazz" * (i%7 == 0)
print (pr if pr else i)
I iterated over range(110) to show that it handles the "FizzBuzzBazz" case correctly.
EDIT: Or we could use lambdas as OP's Ruby code did; this might be more maintainable...
cases = [
lambda n: "Fizz" * (n%3 == 0),
lambda n: "Buzz" * (n%5 == 0),
lambda n: "Bazz" * (n%7 == 0) ]
for i in range(110):
pr = ""
for case in cases:
pr += case(i)
print (pr if pr else i)
I agree. Same thing in JS. Also, this is precisely what I thought to do when he started asking about 7 and 11.
(function fizzbuzz(n, cases) {
var i = 0;
while (i++ < n) {
console.log(cases.reduce(function (prev, c) {
return i % c[0] ? prev : (prev || '') + c[1];
}, null) || i);
}
}(100, [[3, 'Fizz'], [5, 'Buzz'], [7, 'Bazz'], [11, 'Boo'], [13, 'Blip']]));
Excuse the terseness, I didn't want to make this post too lengthy, but it's still quite readable, IMO. Array.reduce was designed to solve problems like this.
The Ruby solution author quotes as ideal and impressive seems way overblown to me. And I don't buy that ,,it would probably be dismissed as “overly complex” by younger programmers'' because it is exactly what I would consider perfect approach... few years ago. Since then I learned the value of simplicity, and abstractions with adequate flexibility.
Firstly, the ruby code was produced (admittedly from my memory) from someone on the tail end of a 5 hour interview process, of which I was not the first technical interviewer. It is exceptional in that context.
Secondly, I feel like you (and aristus in his/her code snippet above) missed the secondary point of my post. Please consider that monoids and Maybe are capturing a higher level pattern in a way that we can compose. To write a classical for loop and toss in conditionals and whatnot is to descend to exactly the same depths as the Ruby code you say is not "simple".
Basically, you say one is "simple" and the other is not is you picking up on what you're comfortable with in Python. Both are complex in the same way; the Python just makes a marginally better choice in how to represent the rules (as data).
If you'd like to see how I'd take that piece of code and golf it to include that feature, I'm happy to oblige. I originally was going to post that, but cut it because the post was already overlong and the point is to explore the unusual patterns fp is capturing.
Here is my bullshit golfing derivative, though:
{-# LANGUAGE MonadComprehensions #-}
module Main where
import Control.Applicative
import Data.Monoid
import Data.Maybe
import System.Environment
factors = [(3, "fizz"), (5, "buzz"), (7, "bazz")]
rules = [\i -> [res | i `rem` fac == 0] | (fac,res) <- factors]
fizzbuzz d i = fromMaybe (d i) $ mconcat (rules <*> pure i)
main = do
upTo <- fmap (maybe 100 read . listToMaybe) getArgs
mapM_ putStrLn [ fizzbuzz show i | i <- [1..upTo] ]
I'd like to understand more that second point. How is Maybe not morally equivalent to a conditional? If my ifs were in a separate function that would also work, no?
> How is Maybe not morally equivalent to a conditional?
Maybe is a way to represent conditionals that is amenable to higher order patterns. if-then-else conditionals are simply that, whereas Maybe is amenable to Monad, Monoid, and Applicative functor laws. So when we pull out mconcat or <> or mappend, we're actually talking about a higher order pattern.
As I mentioned in the article, we could take that same fizzbuzz code and modify it in many ways under the monoid rules. For example, a different harness in the main function could use Map String Integer and completely change the behavior with the same fizzbuzz function.
I had a bit more time to spare and thought I'd give you an example of this. First, let's rewrite fizzbuzz to totally get rid of any mention Maybe; we'll deal with it outside:
fizzbuzz d i = mconcat (rules <*> pure i) <|> pure (d i)
This is written in terms of an Applicative Functor and a Monoid.
And then can just do some haskell things. I've done less golfing here and more type signatures to make the code more approachable.
{-# LANGUAGE MonadComprehensions, OverlappingInstances, FlexibleInstances#-}
module Main where
import Control.Applicative
import Data.Monoid
import Control.Monad
import Data.Maybe
import Data.List
import qualified Data.HashMap.Strict as M
import System.Environment
-- Let's make it clear what we're working with.
type Counter = M.HashMap String Integer
-- We want an instance slightly different from the default.
instance Monoid Counter where
mempty = M.empty
mappend = M.unionWith (+)
factors = [(3, "fizz"), (5, "buzz"), (7, "bazz")]
-- Our rule function is slightly different.
-- Not unexpected, since our logic has changed. But we could generalize
-- this further!
rules :: [(Integer -> Maybe Counter)]
rules = [\i -> [M.singleton res 1 | i `rem` fac == 0] | (fac,res) <- factors]
-- Fizzbuzz remains unchanged.
fizzbuzz d i = mconcat (rules <*> pure i) <|> pure (d i)
main = do
upTo <- fmap (maybe 100 read . listToMaybe) getArgs
let results = foldl' mappend mempty [ fromJust $ fizzbuzz (const mempty) i | i <- [1..upTo] ]
putStrLn $ show results
And then a typical session:
~/P/h/fb-toys > time ./fbg3 10000000
fromList [("bazz",1428571),("fizz",3333333),("buzz",2000000)]
3.58 real 3.54 user 0.03 sys
Which is a pretty expensive way to avoid doing algebra, but the point is that we're talking about very high level patterns here for fizzbuzz. Fizzbuzz is probably a bad name here, it's more like mergeOptionalPatterns. I'm willing to bet if I dug a round a bit in parser combinator libraries I could find something that does nearly exactly this.
I confess I had to play with it a bit to get it to play nice with large inputs.
Halfway through the article, I'd write the Python as:
for i in xrange(1, 101):
s = ""
if not i % 3:
s += "Fizz"
if not i % 5:
s += "Buzz"
if not i % 7:
s += "Bazz"
print s or i
Which can be shortened to:
for i in xrange(1, 101):
s = ""
s += "Fizz" if i % 3 == 0 else ""
s += "Buzz" if i % 5 == 0 else ""
s += "Bazz" if i % 7 == 0 else ""
print s or i
Finishing the article inspired:
print "\n".join(
"".join(filter(None, ("Fizz" if i % 3 == 0 else None,
"Buzz" if i % 5 == 0 else None,
"Bazz" if i % 7 == 0 else None)))
or str(i) for i in xrange(1, 101))
I understood from near the very beginning that this wasn't really about Fizzbuzz. All the way through the post, I was waiting for the author to get a real world example instead of Fizzbuzz. Yet, I got to the end of the post and realized it was already pretty long.
I think in talking about programming, we are often hindered by the fact that it is much more complicated than our brains can handle. Our only choices are to write novel length treatments of real programs or short story length posts about a toy program, or just the tiniest piece of a real program.
Which is all to say, as sick of hearing about Fizzbuzz as I am, I'm glad there are silly little examples like it that we all know. Even though it was ostensibly about interviewing, that was a much clearer introduction to monoids than most. I think it was largely because it was in reference to Fizzbuzz: something very concrete with which we're all familiar.
Too many introductions to Haskell's abstractions are too abstract. Good on the author for finding away around that.
What sort of surprised me as I shopped my copy around and showed people the python-add-one-factor example is that even programmers I consider experts didn't realize how quickly the conditional cascade blows up. It's easy to miss. And I looked around for people to mention this in blog posts, but almost no one does. So I feel like there's a bit of life left in the old fizzbuzz yet.
I think the non-blown cascade is exactly what makes fizzbuzz aggravating. With three noises, the linear solution clearly dominates. With two, the exponential is actually shorter -- but feels unclean.
I've been programming some game servers, and I have the same problem with guaranteed two-player games; I feel dirty hard-coding the logic to assume two players, yet making it general enough for N players makes it absurdly more complex for no gain, which is a net loss.
(And yes, before anyone pops in, these are guaranteed two-player games. Of all the rules of the games in question which have changed over time, that is the one rock-solid constant which will not change in this application.)
Forget Fizzbuzz, we get candidates that cannot reverse a string (in their language of choice). A friend of mine just told me he uses the question "What is the hex number that comes after 'F'" as his first "weed-out" technical question. It boggles the mind.
Because I tend to get really nervous in interviews and consequently don't interview all that well most of the time, I try to be sensitive to people like me. I prefer to start very simple and sort of gradually increase the pressure until I can find a backing off point.
I haven't observed any correlation between being nervous and underperforming in answering technical questions. How do people get through math exams in college? If anything, I'd expect answers to non-technical questions to be far more influenced by the artificiality and unfamiliarity of the interview situation, being judged by a stranger, high-stakes, and everything.
I actually got a lower GPA in college than I could have because of test anxiety issues in college, and I've since learned that it is hardly a rare condition.
Bless you. I get extremely nervous in interviews as well. One that came right out of the gate with difficult questions (not saying string reversal is) would crush me.
If you cannot reverse a string under pressure, you are likely unqualified for the job you are applying for. At least in my job, there is often more pressure than "write a string reverse function in any language you like in 10 minutes."
What a load of bollocks. If your job is to write methods to reverse strings then you are unqualified, otherwise I wouldn't give a shit whether a candidate can reverse a string. Why would I want to know if someone can reverse a string if the job is to maintain a web app?
A few questions for you. How old are you? Have you hired developers before? Truthfully, have you ever had to reverse a string under pressure at your current job.
Are you kidding? reversing a string in your language of choice is far too trivial. If you can't do it you really aren't ready to be working in the industry.
It's not about whether they can do it or not. It's about why knowing a skill that will take even a non-developer ten seconds to Google makes someone incapable of functioning as a developer.
More often than not the people that spout that kind of nonsense are twenty-something junior developers that read a bit of HN and have decided that they know better than everyone else about what makes a good programmer.
Why would you test if someone can reverse a string if you're hiring someone to maintain and build on some shitty web app?
It takes someone who doesn't speak English ten seconds to look up the past tense of the verb "bring" - this doesn't mean you'd want to hire as a journalist someone who seems to think the answer is "bringed"
Doing a string reverse is so trivial that it's not about needing that skill, but rather what not being able to do that speaks about the candidate's general aptitude and skill level.
"Why would you test if someone can reverse a string if you're hiring someone to maintain and build on some shitty web app?"
If that question can't be answered then your opinion on someones skill level is irrelevant. Also, if you've got time, answer the questions in my first post:
"How old are you? Have you hired developers before? Truthfully, have you ever had to reverse a string under pressure at your current job."
Because anyone who can't do this almost certainly doesn't have the ability to program any software system well, some shitty web app or otherwise. You're not trying to fill out a remedial CS course in community college, you're trying to get someone to program real things real people use.
The only way this question wouldn't be useful is if you had a good filter such that almost everyone who gets to that point would answer that question correctly. But apparently, this is not the case. There are people who fail such tests (despite apparently good paper qualifications and ability to talk BS). It's fairly important that you not let through such imposters.
And yes I've hired people and all of them have had to answer much more complex algorithmic questions to get the job and all of them turned out to be competent. And "have you ever had to" questions are ingenuous - interview settings are never going to be replicated exactly in a work setting, unless your job is to go around to be interviewed for different jobs. Good interview questions don't simulate work setting - they extract most critical work-relevant information about the candidate without wasting time.
"Because anyone who can't do this almost certainly doesn't have the ability to program any software system well, some shitty web app or otherwise. You're not trying to fill out a remedial CS course in community college, you're trying to get someone to program real things real people use."
So...I'm hiring someone that can reverse a string because this demonstrates that they can program real things that real people use?
"And yes I've hired people and all of them have had to answer much more complex algorithmic questions to get the job and all of them turned out to be competent."
Out of interest, what did this job entail? I assume that you quizzed them on their knowledge of basic algorithms because they are required to write them, right?
String reversal, and all the other common interview questions are simply a microcosm of what a developer does day to day. Programming is about comprehending abstractions and composing them into a greater whole that solves a specified business problem. Reversing a string is exercising those same mental faculties. There is literally no difference between working out the steps to reversing a string and working out the steps to pulling data from a database, transforming it, and displaying it on the screen, except that for the second example one can Google then copy/paste about 90% of it. Asking "reverse a string" type questions will weed out the Google-jockies (obviously there's nothing wrong with Googling for an answer, but if you have to Google for everything you're in the wrong line of work).
Bullshit. The only reason why people ask these things is because big software houses do so, and for some reason they think that their shitty web app needs a Google quality "rockstar developer".
The difference between reversing a string and pulling data from a database are, literally, completely different tasks. The killer is that the latter is something that a candidate will do in a job.
You're more than welcome to ask those questions, and you're probably likely to weed out the lowest common denominator types. In my experience, they're a waste of time and the best possible interview you can give to a developer is a frank face-to-face discussion about programming, and then getting them to work on a test project for two hours from your own code base. If they can do that much they can program.
>The difference between reversing a string and pulling data from a database are, literally, completely different tasks. The killer is that the latter is something that a candidate will do in a job.
And this is where you misunderstand the purpose of these types of questions. Programming is a very dynamic field where you're likely be required to solve novel problems daily (not novel in the grand scheme of programming, but novel to you). You need the mental flexibility and fluidity to solve problems you haven't entirely encountered before. Testing specifically what you do at the job is the wrong approach. You need to test the required mental faculties to solve novel problems. Programming is not pulling data from a database, writing glue code, and shoving it to the screen. It's solving new problems every day.
> The difference between reversing a string and pulling data from a database are, literally, completely different tasks. The killer is that the latter is something that a candidate will do in a job.
Why can't you do both? Seriously? Pulling data from a database is orders of magnitude harder to get right than reversing a string.
That's not the point. You have a finite amount of time in an interview, so why on earth would you ask a question regarding a meaningless skill that the candidate will use maybe once in their entire time at the company when you can ask something that, you know, might actually tell you if they're a good programmer or not.
Being able to recognise the correct answer in the sea of dross that is search engine results pages is better.
Being able to take the correct answer, and adapt it to what you need, and plugging it into what you already have, is best.
Do you really want to employ people who say they have X years of experience doing something but who cannot do a very simple task in the choice of their language? That's not a stupid trick gimmick question - there's no "gotcha" there. Perhaps we're getting caught up on the specific "reverse a string". Substitute that for something relevant to maintaining and building on a shitty web app.
Out of curiosity, when the interviewer asks you to reverse a string in your language, say Ruby, are you supposed to use existing algorithms/methods built-in in that language, or to flip each character using a for-loop? In Ruby, it's really a single method called reverse will do the trick, i.e., "hello".reverse gives "olleh". I'm trying to make sense of the reason why such a question is asked.
No, the expectation is that you'll use a loop or recursion. If you use an existing method/function, the interviewer will kindly ask you to implement that yourself.
The general reason why such a question is asked is because interviewers don't have an infinite amount of time and there's no point in considering any candidate who can't do this. It's a way to weed out ridiculously bad candidates so that you can focus more on good candidates.
Explain. If you can't produce a god damned string reversal function in 10 minutes, why would I believe you can produce a bug fix to your own, complex code in a few hours? That's what interviewers are considering: the interview is a proxy to see if you can come close to the requirements of the job.
I can program a string reversal with my eyes closed, tied up in chains, upside-down, while holding my breath. Or whatever.
The point is more that for a person with a given skill level, interviews can be stressful and make them perform below that skill level. I try to structure my interviews such that people get more comfortable and I don't ram their head into a metaphorical wall. I get all the same information, but without all those hard feelings.
Can you get in the zone with people watching and judging you intently? If you can't get into the zone with only a couple of people watching you, how will you get into the zone when the whole company is depending on your bug fix?
The pressure of judgement is not the same as the work pressure of a stressful scenario. It's why public speaking and job interviews are among the most feared activities people go through, but people consider meetings boring and mundane.
Easy, the company that is depending on me is not physically there, watching and judging me. Sure they probably are watching and judging me, but as long they're not physically present that makes all the difference to my mind...
To be fair though, reversing a string is not the kind of thing a lot of people deal with - there's usually a library function. Now they should be able to puzzle it out, but if it takes them a moment I'd be generous because I at least put it into the box of "that sounds easy, but oh wait a minute it's slightly different from my usual dev pattern".
To put it another way - when I code it's rarely different from writing to me; code is simply an expression of thought. But when you ask me to do something that seems a bit artificial (reverse a string) it's like asking me to write a haiku - I can do it, but give me a moment while I count out my syllables.
I assume the question is something along the lines of "Implement a strrev in the language of your choice", not "Use the strrev equivalent in the language of your choice".
If I was the interviewer in that case I would probably accept that, with note that the person seemed clever. Then I would ask another harder question that I would hope would trip them up if they didn't understand iteration.
I agree that Fizzbuzz can be a more interesting example of how to write code without repetition. While the author suggests that languages such as Haskell provide a unique advantage, the deciding question seems to be the availability of pre-built abstractions. Consider the following solution in Python:
for i in xrange(1,101): print (('' if i%3 else 'Fizz')+('' if i%5 else 'Buzz')) or i
or the even more general:
mapping={3:'Fizz',5:'Buzz'}
for i in xrange(1,101):
print ''.join(['' if i%x else mapping[x] for x in mapping]) or i
We can even do this in C though to write something as extensible as the second would require writing more helper functions than I can justify for this brief comment:
#include<string.h>
#include<stdio.h>
int main(){
int i;
char s[4];
char n[3];
for (i=1;i<101;i++){
sprintf(n,"%d",i)
s=strcat((i%3)?"":"Fuzz",(i%5)?"":"Buzz")
printf("%s",(strlen(s))?s:n)
}
return 0;
}
I'm not sure even hell will take you after doing what you just did. There is no metaphysical consequence great enough save to have experienced the writing of that.
You can nest NoisemakerPairs to add more Noisemakers. But good luck with strings longer than 4 characters… for some absurd reason, string literals can't be template parameters. Go figure.
That's like making the universe crazier one electron at a time.
But yes, it's just about having the right abstractions. What's nice about the Haskell solution is that the right abstractions make the code much nicer (to my eye).
I honestly don't understand why that's crazy. Empty string evaluates as falsey, so the "or" picks the 10, which is obviously equal to 10. Or am I missing something?
It just seems like a bad choice. The empty string being falsey is... very arbitrary to me. It seems like it's strictly a perl legacy thing that should be (but cannot be) reconsidered.
About as far as I am willing to go is nil punning.
> The empty string being falsey is... very arbitrary to me.
It's extremely useful in a lot of contexts. The basic logic is that an empty string is an empty container, and empty containers are false, so you can test for them more easily.
Breaks the spec: 'Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.'
Actually, this spec is ambiguous, too. For multiples of five, do we print the number AND "Buzz" or just "Buzz" ? Similarly for multiples of 15.
I thought the OP was a joke, BTW. It had to be, doesn't it? If somebody thought of this problem and solution the way the job candidate did, why on earth would one want to hire somebody who loves complexity for its own sake?
To me, the following is the clearest way to express this (in C anyway). I suppose it says a lot about me! lol
Well balls to you too, I guess? Or not. We're all just trying to have a good time here. And from the look of this thread, people are having fun.
I think golfing fizzbuzz into an unreadable mess sorta misses the point, but most people here are software engineers because they love to code and build stuff. So let's enjoy the moment.
> why on earth would one want to hire somebody who loves complexity for its own sake?
It's not complexity for its own stake. I daresay the functional patterns tend to be simpler. Certainly moreso than things like Visitor or AbstractFactory.
> To me, the following is the clearest way to express this (in C anyway). I suppose it says a lot about me!
Your version is almost identical to the Ruby version I listed. And it's an appropriate solution for C.
Yes, if we're all trying to have a good time, we'd shoot heroin and be done with it. ;-) IMHO, we are group sharing to learn better ways to build startups, small businesses, and to better ourselves, co-workers, and companies with best practices - and to do it with fun and a twinkle in our eyes.
I did look at that wordmark comic -- I believe I ask a credible question: If complexity buys you nothing, then why make something complex? And, believe me, if you are thinking "Visitor" or "AbstractFactory" then I have already lost you. ;-)
Thank you for your comment about my C version. I wanted to make sure it was extensible and quite obvious -- because I write code to be read by humans as my 2nd goal (my first is that it works & meets the spec!). Knuth's Literate Programming taught me that. The other thing about that C solution is that it's simple and many people can understand it. One does not need to know the difference between a monad and a gonad to "get it".
My overall comment: Complexity: when required; otherwise, choose simple. Believe me, I have learned this lesson over and over again during my 45 years of programming.
> If complexity buys you nothing, then why make something complex? And, believe me, if you are thinking "Visitor" or "AbstractFactory" then I have already lost you. ;-)
I think maybe the disconnect here is that I work on products with a much larger scale than you may be accustomed to. My idea of complexity begins to blossom at scales past many boxes. For me, 25 interconnected machines is barely enough to register as a "big system". So perhaps I am just desensitized?
Abstraction is generally trying to find ways to mitigate complexity, not embrace it. At some point we need to have sympathy for the machines we run on to have efficient code, but at the same time you and I need to find good perspectives to write code at.
A more concrete example of this is my advice that people building distributed systems take a cue from Boundary and have systems rendezvous in Zookeeper rather than just using something like hardwired ports and /etc/hosts. You may think that "sounds complex" but it's actually a very simple solutin from our perspective as users of Zookeeper. It ends up being pretty nice the next time a giant storm takes out half of us-east-1 and you're frantically re-provisioning boxes to keep your service up.
Monoids are that kind of choice at the programming level.
> Complexity: when required; otherwise, choose simple. Believe me, I have learned this lesson over and over again during my 45 years of programming.
I think c_wraith's monoid example made the program simpler, cleaner, and less bug prone. If you think monoids are a "complex" idea, then my blog post has failed profoundly and I apologize for wasting your time.
I believe that efforts using trivial examples to show how complexity can be managed via certain abstractions are doomed to fail.
And that's a pity, because it makes abstractions harder to explain in a proper context.
You need an example where the complexity at least rears its ugly head before the abstraction starts to shine.
For many people, especially those not familiar with it at the time of the reading, the simple example using a manage huge complexity abstraction reeks of "complexity for its own sake" or "killing flies with a cannon" or "premature optimization" and it's natural because the example is not showing the strengths of the abstraction, rather its weaknesses.
> I believe that efforts using trivial examples to show how complexity can be managed via certain abstractions are doomed to fail.
I've received 5 emails and about 10 tweets from people saying, "I think I understand monoids better because your tutorial sat at the right level of abstraction and overhead for me to get."
So, I think I succeeded for 15 people. Which I am pretty damn happy about. Because whenever I write one of these I pretty much assume everyone will misread, misunderstand, or ignore 3/4 of my post. It's just my experience.
People who call the monoid abstraction "confusing" and "complexity for its own sake"... I just have to assume these people can't handle the discomfort of learning new concepts anymore and write them off as unreachable. It's not like I owe them anything. Because I'm pretty sure it's actually not confusing at all, it's just a different perspective.
The real irony is that for awhile I was deep in the OO and Ruby culture and said a lot of these same things. You can find them online if you know where to look. So I understand where these people are coming from and know that without their cooperation, I cannot explain anything at all.
FWIW, you can count me in. I've understood monoids better thanks to your article.
My comment was not a jab at your article, but a comment on how hard it is to find the proper abstraction level.
And that I fully understand people who'd say it is making things more complex for its own sake, and I'm not sure it necessarily is because they don't want to learn.
You can generally approximate higher order functions in almost every OOP language. See C++ before it got lambdas, they have a pattern. See also java's anonymous inner classes, which are often used with the Runnable interface for that rule.
In the Python example, wouldn't you need to make sure the x values from mapping are sorted, since dictionaries are unordered? Change,
for x in mapping
to
for x in sorted(mapping)
I wrote a JS one-liner solution the other day, though JS isn't quite as concise as Haskell:
function fizzbuzz (n) { return new Array(n + 1).join().split(',').map(function (j, i) { return (i % 3 ? '' : ' fizz') + (i % 5 ? '' : ' buzz') || ' ' + i; }).slice(1).join().slice(1); }
185 characters
Edit: If I move from a general solution to only 1-100 and remove spaces and semicolons:
new Array(101).join().split(',').map(function(j, i){return (i%3?'':' fizz')+(i%5?'':' buzz')||' '+i}).slice(1).join().slice(1)
Down to 126, but a lot of that is related to precision with the whitespace and handling a JS quirk with map on undefined values; without the extra joins/split/slices, it goes down to 83 characters (but also doesn't work).
That max looks suspicious. It (ab)uses the fact that the strings "Fizz", "Buzz" compare higher than any printed integer. If you try to extend it to (7, " Bazz") it fails, because " " comes before digits.
# 97 characters
for my $i (1..100) { say join("", map { {3=>'fizz',5=>'buzz'}->{$_} unless $i % $_ } 3,5) || $i }
# 81, if those pesky spaces are removed
for my$i(1..100){say join("",map{{3=>'fizz',5=>'buzz'}->{$_}unless$i%$_}3,5)||$i}
The Ruby example that you recommending hiring because of, is overkill. Here is a better Ruby example:
(1..100).each do |i|
o = ""
o.concat("Fizz") if i % 3 == 0
o.concat("Buzz") if i % 5 == 0
o.concat("Bazz") if i % 7 == 0
o.concat(i.to_s) if o.empty?
puts o
end
Great article. The title is a bit misleading though. I thought this was sort of a counter-argument to the 'programmers cannot program', but introduces monoids right at the end.
Anyway, FizzBuzz and the like are the kinds of questions you would probably ask to a new graduate because frankly, there is nothing else to ask from them. They have no related experience for the most part.
For experienced ones, the way our company (http://aelogica.com) identifies good candidates is via a day of pair-programming. Technical knowledge is just part of the package, and any kind of problem solving will not address that.
"When you really boil it down to its implementation, FizzBuzz is something of an irritating program. I’m not sure how much the author of the problem really thought about FizzBuzz, but it turns out it’s difficult to express well with the tools available to most imperative programming languages..."
Nonsense.. you call a simple loop with a couple conditions difficult?
This seems overcomplicated. Why wrap String (which is already a monoid) inside Maybe? You can just use concat; if the result is the empty string then print the number. If you want it to work for any monoid, then use mconcat, and test for equality to mempty.
And why introduce monad comprehensions if you're just introducing monoids?
> Why wrap String (which is already a monoid) inside Maybe?
Because it's a bit easier to write the tail end of the logic in generic terms. You can go talk to c_wraith on Freenode#haskell if you want.
> You can just use concat; if the result is the empty string then print the number. If you want it to work for any monoid, then use mconcat, and test for equality to mempty.
You can find other discussions about why flexibility is important.
> And why introduce monad comprehensions if you're just introducing monoids?
Because it's a really nice syntax? Compared to explicitly using guard, anyways.
First of all a modulo is ultra expensive, one does not simply modulo 15 when they already modulo 3 and 5.
The proper structure is if(3){if(5)}elif(5){}else{},unless anyone has a better proposition.
While of course it is possible to define abstractions that handle fizzbuzzing anything for any number, it's clear that the monoid way is a bad overweight bloated approach.
Second the example written is code bloat à la java, writing tons of stuff for no reason.
Third Ruby is a beta prototype language that doesn't have any production ready implementation.
Lastly functional and procedural programming enable you to work at the highest level of abstraction you can think of, whereas OO tends to lock you at a specific abstraction level, which is pretty low and not adapted to most cases.
I have to admit it's impressive how such a simple test can show so many failures in people's deep understanding of programming.
This is another example of someone who I think looked at my code examples but didn't read the post. Fair enough, I guess.
> While of course it is possible to define abstractions that handle fizzbuzzing anything for any number, it's clear that the monoid way is a bad overweight bloated approach.
Why? It is not computationally expensive, and it avoids excess modulo operations. All the dispatching work gets figured out at compile time and you end up with pretty much the same sorts of string concatenation costs you get in every other implementation.
Do you mean to suggest that it is conceptually expensive?
> Lastly functional and procedural programming enable you to work at the highest level of abstraction you can think of, whereas OO tends to lock you at a specific abstraction level, which is pretty low and not adapted to most cases.
I actually agree with this, although I sort of question procedural's place on the totem. I suppose stuff like Forth suggests you're right.
> Bloated, as in, takes way too much space for what it is.
> Additionally, it does _not_ avoid excess mod operations, _and_ the implementation is _broken_ because it won't print fizzbuzz for the 15.
If we're talking about the Haskell version here (I can't tell?), you're wrong on all counts.
• It is about as short as any reasonable impl I've seen of FizzBuzzBazz. Not long at all.
• And it does print out correctly for 15.
• And it does NOT do excess operations. It does exactly one modulo per factor.
Do you not understand how it works? I detail it pretty closely in the post; please look again.
The second ruby version _is_ long, but does not perform excess operations (unless you're complaining about Ruby's implementation of lambdas, which is out of scope). As I said both in my article and in this discussion; coding during an interview is tricky and that's not a bad effort.
Fianlly, the naive if-chain extensions are deliberately wrong to extend, but my initial research and experience is that most programmers don't realize that until they try it once. But then, they're lifted right from Rosetta Code so they're hardly unrepresentative of what people consider an "unacceptable" version of Fizzbuzz.
We're five years into this, and here's yet another weekly column from a person who has just heard about it and is champing at the bit to prove both he can write FizzBuzz and all the other implementations are not as good as his. It will be a miracle if this thread doesn't turn into a chain of "even better" solutions, like all the other threads that came before it.
In this week's installment, the variation where it is claimed that common production languages are inadequate for a problem of this complexity, and the tool stack should be shifted to languages supporting monads... er monoids? Sigh.
I would prefer to force candidates to implement FizzBuzz in a language invented solely for purposes of that interview (and which will never be used again). This places all candidates on the same level.
It's probably a good thing I don't interview people for programming positions...
You have so little time in an interview to learn so much you can't afford to lose the time to both learn whether they can do FizzBuzz (or whatever other problem) and whether they can manipulate some language of interest, when you could be learning both
The purpose of an interview isn't to be abstractly "fair", it's to find the best candidates for the job. You choose what "best" is (which is to say, please don't put words in my mouth about "only interviewing for the exact skillset" or whatever... you choose what is best, whatever that is). My preferred approach is to give the problem, then let the candidate write in whatever language they choose. If they flail in their putatively favorite language with which they've putatively been working for 4 years... well... I've certainly learned some very important things in those few moments.
I thought it was pretty clear based on the second sentence that the comment was intended as a joke. Do I really need to start adding /jk to the end of my comments?