haskell 基础题解(56)

控制台月历

【题目】给定年份,月份,在控制台输出该月的月历
比如,2019年9月的月历:
在这里插入图片描述
很明显,这个问题的要点是:对给定的年、月,需要知道这个月有多少天,还需要知道它的1号是星期几。

可以参考以前关于日期问题的算法。

----判断某个年份是否为闰年
b闰年 :: Int -> Bool
b闰年 year | year `mod` 400 == 0 = True
           | year `mod` 100 == 0 = False
           | year `mod` 4   == 0 = True
           | otherwise           = False

----从公元19001月开始,逐月的天数无限表
monthDays :: [Int]
monthDays = concat [ [31,f x,31,30,31,30,31,31,30,31,30,31] | x<-[1900..] ]
    where f x = if b闰年 x then 29 else 28

----190011日 到 y年m月之前过去的天数
days :: Int -> Int -> Int
days y m = let n = (y-1900) * 12 + m - 1
    in sum $ take n monthDays

---已知190011日是星期1,求之后的 y年 m月 1日是星期几
monthWeek :: Int -> Int -> Int
monthWeek y m = days y m `mod` 7

---单行按每行n个元素折成多行
foldLine :: Int -> [a] -> [[a]] 
foldLine n xs 
    | length xs <= n = [xs]
    | otherwise = take n xs : foldLine n (drop n xs)

---把一个串前边添空格,补到指定的长度为止
fillSpace :: Int -> String -> String
fillSpace n s 
    | length s >= n = s
    | otherwise = ' ' : fillSpace (n-1) s

----已知 y年 m月,求当月的月历表
calendar :: Int -> Int -> [String]
calendar y m = 
    let passMon = (y-1900) * 12 + m - 1
        thisMon = monthDays !! passMon
        weekBegin = monthWeek y m
        t  = replicate weekBegin " " ++ map show [1..thisMon]
        t2 = foldLine 7 t
        t3 = map (map (fillSpace 2)) t2
        t4 = map unwords t3
    in "Mo Tu We Th Fr Sa Su" : t4

main :: IO ()
main = putStr $ unlines $ calendar 2019 9

这里算日期的基点取了 1900年1月1日,而不是公元元年。
星期的说法本就是后来的事。用今天的星期推公元元年星期是不准的。原因复杂。
我们现行的历法是:格里高利教皇历法。
1752年9月,英国议会决定把原来的恺撒历法改为现行的格里高利历法(200多年前就修定好了,却没人执行),因为这两个历法当前日期差11天,议会决定当年9月3日到到9月13日删去。也就是说9月2日后,直接是9月14日。
所以在日期转换,算星期等问题上,如果日期在1752年9月之前,要小心处理了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值