4 ms·
cases = ( (3, 'Fizz'), (5, 'Buzz'), (7, 'Bazz'), (11, 'Boo'), (13, 'Blip'), ) for i in range(1, 101): out = [] for c in cas
by aristus 14y ago
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.
- just2n 14y agoI 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.
- Xion 14y agoPrecisely. 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. That Ruby code exhibits neither.
- KirinDave 14y agoFirstly, 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] ]
- aristus 14y agoI'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?
- KirinDave 14y ago> 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.
- KirinDave 14y agoI 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.
- Beltiras 14y agoCame here to say this ;) Good job.
- sowhatquestion 14y agoThis 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) That's nine nonblank lines.
- koblas 14y agoJust for fun, another variant - separating logic from data and trying to be pretty stock python. cases = [(3, "Fizz"), (5, "Buzz"), (7, "Bazz")] for i in range(110): pr = ''.join([v[1] * (i % v[0] == 0) for v in cases]) print pr or i
- deadmansshoes 14y agoIt should be possible in a single line of Python.. pip install fizzbuzz And then: import fizzbuzz print fizzbuzz.fizzbuzz() Or for the second solution: print fizzbuzz.fizzbuzzbazz()
- jonp 14y agoA tweetable (140 character) single line of Python: print '\n'.join((lambda x:(''.join(x) if x else str(i)))([w+'zz' for (n,w) in ((3,'Fi'),(5,'Bu'),(7,'Ba')) if i%n==0]) for i in range(1,100))
- js2 14y agoHalfway 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))