カレンダー
勉強会の内容を受けて、以前に書いた19問目のコードを書き直してみました。
算術演算は使わず、カレンダー情報を1次元のリストに格納しています。
西暦 0 年から始めて 1900 年まで読み飛ばすとか、けっこう無駄なことをしていますが、所詮 O(n) なので、以前に書いたコードより速かったりします。
data DayOfWeek = Su | Mo | Tu | We | Th | Fr | Sa deriving (Eq,Ord,Enum,Bounded,Show) main = print $ euler019 1901 2000 euler019 :: Int -> Int -> Int euler019 begin end = length $ filter ((== Su) . snd) $ filter ((== 1) . day) $ takeWhile ((<= end) . year) $ dropWhile ((< begin) . year) calendar where day = snd . snd . fst year = fst . fst calendar :: [((Int, (Int, Int)), DayOfWeek)] calendar = zip (dropWhile ((< 1900) . fst) years) (drop 1 dayOfWeeks) dayOfWeeks :: [DayOfWeek] dayOfWeeks = cycle [minBound..maxBound] years :: [(Int, (Int, Int))] years = concat $ zipWith zip (map repeat [0..]) (cycle y400) where y400 = (months True :) $ tail $ take 400 $ cycle y100 y100 = (months False :) $ tail $ take 100 $ cycle y4 y4 = (months True :) $ tail $ replicate 4 $ months False months :: Bool -> [(Int, Int)] months leap = [ (month, day) | month <- [1..12], day <- days leap month ] days :: Bool -> Int -> [Int] days leap month = [1..last] where last = if month == 2 then if leap then 29 else 28 else if elem month [4,6,9,11] then 30 else 31
3 次元リスト版。勉強会のコードの写経です。なるほど mapAccumL はこう使うのか。
import Data.List data DayOfWeek = Su | Mo | Tu | We | Th | Fr | Sa deriving (Eq,Ord,Enum,Bounded,Show) main = print $ euler019 1901 2000 euler019 :: Int -> Int -> Int euler019 begin end = length $ filter (== Su) $ map head $ concat $ take (end - begin + 1) $ drop (begin - 1900) calendar calendar :: [[[DayOfWeek]]] calendar = snd $ mapAccumL mapYear dayOfWeeks years where mapYear = mapAccumL mapMonth mapMonth = mapAccumL mapDay mapDay (w:ws) _ = (ws, w) dayOfWeeks :: [DayOfWeek] dayOfWeeks = drop 1 $ cycle [minBound..maxBound] years :: [[[()]]] years = map months [1900..] months :: Int -> [[()]] months year = map (days year) [1..12] days :: Int -> Int -> [()] days year month = replicate len () where len = if month == 2 then if leap year then 29 else 28 else if elem month [4,6,9,11] then 30 else 31 leap :: Int -> Bool leap year = (mod year 4 == 0 && mod year 100 /= 0) || (mod year 400 == 0)