You've created a nice, maintainable FizzBuzz Python implementation. What I find really, really interesting is that, in your description of your requirements, you left out "otherwise, print the number." This is what most FizzBuzz interviewees leave out of their implementation. You nailed it in your implementation, but missed it in your plain-English description of the problem.
I'm not trying to "rip up" your post - I think it's instructive of how easy it is to miss a detail in requirements. We always need to be on the lookout for details we missed. Our customers may not care, but the computers that interpret our code certainly do! :)
(And, yes - there are way better implementations than the typical "if 0 == x % 15 then 'fizzbuzz' elseif 0 == x % 5 then 'buzz' elseif 0 == x % 3 then 'fizz' else x" that the interviewer typically sees.)
Finally, a side note - people like to rip on this as a basic-level test, but from a functional perspective, it's a mapping problem. (1 = '1', 2 = '2', 3 = 'fizz', 4 = '4', 5 = 'buzz', etc.) Your if_multiple and n_if_empty functions show that you can abstract a problem into reusable parts. It may appear to be over-engineered to some (and probably would be for an interview), but it shows that you understand the steps involved. Nice post!
Thank you for the feedback.
I am a little confused by your statement, that I left out the "otherwise print the number" statement. I double checked my code and the output was the exact same as both my test output and my first attempts code. Was it in my introduction that I missed the statement or the code itself? Either way its a pretty easy fix.
Oh heck I'm sorry, I misread your comment.
Thank you for pointing that out, I'll update the introduction.
Yeah, you got it. :)
Hmm, I actually like the first one better. I feel like the second one should be better from a modularity standpoint, but I find the first one much more readable and clear as to what it does (if the variables were renamed to something like div_3 and div_5). Perhaps making a nice mix of the two would be good!
I think the biggest flaw in the second example is the output variable. Whenever I see str += otherStr inside of a small for loop, I think that it is building up a string to become longer and longer as a for loop persists (as if you were going to print all of them at once). It's just a common pattern, took me a second to realize that wasn't what you were doing.
str += otherStr
One last thing - I think that perhaps because fizzbuzz is a problem based on conditions, maybe using conditional operators such as if and elif is ok, and maybe a bit more readable.
Still my favourite implementation, because it is completely declarative:
createRule n s = cycle $ replicate (n-1) "" ++ [s]
fizzes = createRule 3 "Fizz"
buzzes = createRule 5 "Buzz"
fizzBuzzes = zipWith (++) fizzes buzzes
numbers = fmap show [1..]
choose a b = if a == "" then b else a
result = zipWith choose fizzBuzzes numbers
main = mapM_ putStrLn $ take 100 result
nice one - here is basically the same (I did not care about the prompt) in Haskell:
module FizzBuzz where
import Data.Function ((&))
import Data.Maybe (fromMaybe)
import Data.Monoid ((<>))
fizzBuzz :: Int -> Int -> [String]
fizzBuzz from to = fizzBuzzer <$> [from .. to]
fizzBuzzer :: Int -> String
fizzBuzzer n = Nothing
<> ifMultiple 3 "Fizz" n
<> ifMultiple 5 "Buzz" n
& nIfEmpty n
ifMultiple :: Int -> String -> Int -> Maybe String
ifMultiple d out n
| n `mod` d == 0 = Just out
| otherwise = Nothing
nIfEmpty :: Int -> Maybe String -> String
nIfEmpty n = fromMaybe (show n)
isn't it nice how that just translates
PS: the n there should probably be refactored into a Reader-Monad :P
Problems like FizzBuzz serve another purpose besides screening for programming proficiency, as I think you've discovered in your second example. They also provide a convenient way to explore different programming paradigms and styles of coding. To that extent, I find your second example very "Pythonic", even if it's not exactly how I'd personally prefer to do it. (Unsurprisingly, while I do code quite a bit in Python, it's almost never the language I reach for if I have a choice.)
If you (or anyone else) are interested in more of these kinds of problems:
Finally, there's also the entire Rosetta Code wiki that covers a large number of algorithms, from simple to complex, in a wide variety of different languages.
Well that's a way to over-engineer a simple programming test.
I disagree. I'd much rather debug code from a person who writes code in the second way than the first. For me, at least, posts like this are great little learning tools to help me understand the fundamentals better...
I second this, this shows the fundamentals and importance of clean code. Making it clean makes it readable for others.
I'll admit, I went a bit overboard with the argparser. And I could have named the functions better. What I was looking to do with the second version was create a version that could relatively easily be updated for more complicated versions of the problem. For example if the interviewer wanted me to revise it so that it printed fizz on every multiple of 7.
If you have any suggestions on how I could simplify my code, I would be more than happy to potentially incorporate them into fizzbuzz3.
This comment was not constructive. Please consider a more helpful/thoughtful way to provide feedback in future comments.
I consider the second one to be overly abstracted. The first one is far simpler. It implements the requirements fully without any overhead. Planning for being adaptable is part of YAGNI syndrome. Things are easy to refactor when new requirements are added. Adding complexity in advance is counter-productive.
I feel it is over engineered, though i like such unique solutions practically this should not hit the production.
We're a place where coders share, stay up-to-date and grow their careers.
We strive for transparency and don't collect excess data.