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.
And then can just do some haskell things. I've done less golfing here and more type signatures to make the code more approachable.
And then a typical session: 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.