Why do most implementations of FizzBuzz special case % 15? I haven't ever really understood this. Maybe it's just my math-y background, but it always seemed to me you should just check mod 3 and mod 5 without an else between them, concatenating Fizz and Buzz.
Can anyone else comment on this? Most canonical FizzBuzz programs special case 15, and I don't get it.
Well, its more direct in that, while it increases the number of branches, it minimizes the number of statements executed on any branch. It's also the solution that maps most directly to the problem statement, and, absent a strong technical reason to do otherwise, a direct mapping from requirements to code is a good thing.
I disagree that it maps most directly to the problem statement. You're performing a common factor computation in your mind, which may be more difficult given numbers other than 3 and 5. In my opinion, pattern matching offers the most direct solution and comes with an abundance of compiler optimizations. Here's an example in Rust...
for i in range(1i, 101) {
match (i % 3, i % 5) {
(0, 0) => println!("Fizzbuzz"),
(0, _) => println!("Fizz"),
(_, 0) => println!("Buzz"),
_ => println!("{}", i),
}
}
> I disagree that it maps most directly to the problem statement. You're performing a common factor computation in your mind, which may be more difficult given numbers other than 3 and 5.
Well, sure, explicitly calling out i % 15 rather than (i % 3) && (i % 5) or the equivalent has that problem.
> In my opinion, pattern matching offers the most direct solution and comes with an abundance of compiler optimizations.
Pattern matching is not available in many languages, but, sure, where its available, its a great choice. Note that this still has a distinct case for the case where both % 3 and % 5 are true, rather than just testing those cases independently and sequentially and concatenating, so I think it falls into the general class of solutions I was describing.
The solution in Haskell is quite clean, I believe.
fizzBuzz n
| n `mod` 15 == 0 = "FizzBuzz"
| n `mod` 3 == 0 = "Fizz"
| n `mod` 5 == 0 = "Buzz"
| otherwise = show n
main = mapM_ (print . fizzBuzz) [1..100]
I agree with you about generalizing pattern matching for less simple cases. Your example brought to mind view patterns, about which Oliver O'Charles had a nice writeup recently [1]. Nifty little extension.
let buzzer number =
match number with
| i when i % 3 = 0 && i % 5 = 0 -> "FizzBuzz"
| i when i % 3 = 0 -> "Fizz"
| i when i % 5 = 0 -> "Buzz"
| i -> (sprintf "%i" i)
for i = 1 to 100 do
printfn "%s" (buzzer i)
just because 15 HAPPENS to be a concatenation of the output of 3 and the output of 5 today doesn't mean it will be tomorrow. If I said "say Fizz for multiples of 3, Buzz for multiples of 5, and 'this is a silly coding problem' for multiples of 3 and 5 then you'd have to rewrite your code.
Some of us know that clients ALWAYS change their minds, specs are rarely equivalent to the end result, and code against future changes that are trivial to account for in advance.
For any implementation you can manufacture an example which will require a person to re-structure their code.
Additionally, using %15 is not DRY. If the spec changes from saying "Fizz" on multiples of 3 to saying "Fizz" on multiples of 4, then you will have to also update 15->20. If you forget to do this, you have a bug.
The correct implementation is dependent on the problem's context, and such context is not available with the FizzBuzz problem.
Part of my point (which i obviosuly didn't communicate well) is that Fizz concatenated with Buzz is a premature optimization. It's the developer taking advantage of a linguistic coincience. The instructions are to output 3 different strings based on %3, %5, or %3 and %5. I have never seen a set of fizzbuzz instructions that actually specified that the last option should be a concatenation of the 1st two. It's always specified as a 3rd string that people independently notice is a concatenation of the two.
This is absolutely true for real-world coding. However, for the purposes of an exercise, I think the fact that 15 was chosen was not an accident, but is part of the exercise. Does the coder recognize the relationship between the multiples, and recognize the ability to optimize by removing an extraneous comparison?
It's kind of a trivial test for that sort of recognition, but anything as small as FizzBuzz is going to be rather trivial.
As with most of these little tests, the actual question is, "Can you explain your decision, and does your explanation make sense?"
Really, the FizzBuzz test is a check to see if someone is bullshitting when they say they can code. Testing deeper than that is expecting too much of it.
I suppose it's a style issue as to whether an extra mod is better than checking if one of the triggers has passed. I would propose it's more DRY to do it the short way though.
It's not any shorter. The extra check as to whether or not to print the line break cancels out the mod 15 check. In my opinion, it's cleaner to have three conditionals of the same type than two checking mods and a third checking the OR of the first two.
Of course, it can be actually shorter with a goto.
Whether you print the number, Fizz, Buzz or FizzBuzz you are going output a line break, so I'm not sure what you would be checking for. Output \n unconditionally.
Then you call `printf` twice every loop instead of once. `printf` is buffered so you aren't making two system calls, but you are still making two function calls.
Maybe it's a performance/brevity compromise, but the latter is the issue the I addressed. The least-calls solution would probably be to print the whole output as a single string constant.
For the record, I have utterly no math background (hardly passed algebra), but I also agree that checking only 3 and 5 is the better solution and is how I've always written FizzBuzz.
Another thing i dont understand is why people hardcode 15. I would rather write (3 * 5) and let the compiler figure out that 3 * 5=15. This way i think it more clearly states where the number 15 comes from. Any reason to write 15 over (3 * 5)?
I do that too. It seems more in the spirit of the problem. For an alternate problem, in which you are asked to print strings a, b and c (!= a concat b) if the number is divisible by 3, 5 and 15 respectively, it makes sense to special case 15.
Because of the requirement to print the number if it is divisible by neither. Here:
if x isDivisibleBy 3: print "Fizz"
if x isDivisibleBy 5: print "Buzz"
and.. how do I now print x in the neither case? I can't 'else'. I could make a long if not divisible by either if expression, but that's less easy to read than an if/else chain that starts out with 'if divisible by both, print fizzbuzz'.
If fizzbuzz was: Print FizzBuzz for multiples of 15, fizz for multiples of 3, and buzz for multiples of 5, and nothing otherwise, I bet you'd see the above pseudocode far more.
Really? I find that one line far more pleasant than the Python example above it, with variables being assigned, etc. Both are quite clear in their intention, I think, assuming you recognize "join".
I don't even recognize the language of the one-liner, but it makes perfect sense to me as a reader. It looks like a Perlish solution, but the string method is strange, I think. Maybe Perl 6 or Ruby?
The one-liner is valid Python. It's slightly non-idiomatic in that it uses a list-comprehension where a generator expression would do, and uses range instead of xrange (in Python 2.x; in 3.x range is the idiomatic alternative).
Yes, a ternary would be clearer, to me. I wasn't sure what was happening with the * (and thought maybe it was some use of the "whatever star" in Perl 6, when I was thinking it might be Perl 6, though it doesn't look like any use of the whatever star I've seen), but I just assumed it made sense to someone who knew the language well. It's been a decade since I worked in Python with any regularity.
I would probably use a named function call in real code, but the 'text * (expr == 0)' idiom is pretty clear to me as a way to conditionally include text based on whether a condition is true or not.
Can anyone else comment on this? Most canonical FizzBuzz programs special case 15, and I don't get it.