haskell 基礎題解(39)

2021-09-26 07:31:09 字數 3035 閱讀 7284

【題目】有一類這樣的問題,形如:

2023年1月21日 到 2023年5月4日, 一共經過了多少天?

2023年6月6日後的1000天是什麼日期?

2023年4月1日是星期幾?

從建國到 2023年10月1日,有多少個國慶節是星期日?

…不管你使用什麼語言,請不要呼叫任何關於日期的現成api,而是自己編碼解決上述問題。

實際上,就算允許你呼叫日期-時間的api,這類問題往往也很棘手。因為歷史的原因,人類風俗習慣甚至宗教等原因,時間-日期問題總是被搞得很複雜。什麼閏年、閏月、國際日變更、時區、夏令時。。。。等等一堆概念,都反映在api中,讓人頭大。

不過,如果僅為了完成上面說的任務,大可不必殺雞用牛刀。自已寫個也很容易。

可能只需要乙個小竅門:求 a, b 日期差,只需要分別求出 a, b 到公元1年1月1日的日期差就可以了。而這比直接求 a b 的差要更容易。

上個 haskell 碼,求日期差。

b閏年 :: int

->

bool

b閏年 year

|year

`mod`

400==0

=true

|year

`mod`

100==0

=false

|year

`mod`4=

=0=true

| otherwise =

false

--- 求距離公元1年1月1日 過去了多少天

todays :: (

int,

int,

int)

->

inttodays (1,

1,1)

=0todays (y,1,

1)= todays (y-1,

1,1)

+if b閏年 (y-1)

then

366else

365

todays (y,m,1)

= todays (y,1,

1)+sum

(take (m-1)

[31,x,31,30

,31,30

,31,31

,30,31

,30,31

])where x =

if b閏年 y then

29else

28todays (y,m,d)

= todays (y,m,1)

+(d-

1)

這段碼應該不難理解。有了 todays 函式,求 a, b 的日期差就很容易:

todays (2011,5,4) - todays (1949,1,21)

這是一種很通用的思路。多數時間、日期系統都是這樣內部表示的。

比如: excel,實際上它的資料只有兩種:字串或者數值。

當它儲存日期時,實際上存的是 距離 1970 年 1 月 1 日 午夜的天數。只不過它用的是浮點數。其小數部分是:該時刻在一天中的位置。

我們把 excel 的格式設定為日期並不會改變它的真值,只是改變了真值的表達形式。(這個思路對軟體設計十分重要。稱為模型+檢視)

問題雖說解決了,但不是太 haskell, 還是有點像 c 語言那樣處理的影子(我們可不是要同c語言決裂,只是為了訓練函式式地思考問題)。

現在考慮到這樣乙個問題: 究竟是什麼使得日期問題看起來這麼複雜呢?

是每個月的天數不同!而且2月份天數還不穩定!!

但是: 1 年有12個月,這個是不變的!! 年分不應該跟它們一起混,被月給帶壞了啊。

我們計畫用 haskell 構造乙個 從公元1年1月1起,每個月有多少天的無限列表。只有月,沒有年!無限列表!! 說幹就幹:

--從公元1年1月開始,逐月的天數無限表

monthdays :: [

int]

monthdays = concat [[31

,f x,31,

30,31,

30,31,

31,30,

31,30,

31]| x<-[

1..]]

where f x =

if b閏年 x then

29else

28--- 求距離公元1年1月1日 過去了多少天

todays' :: (int, int, int) -> int

todays'

(y,m,d)

= let passmon =

(y-1)*

12+(m-1)in

sum(take passmon monthdays)

+(d -1)

---上面函式的逆函式,距離公元1年1月1日的天數 轉為 日期

todate :: int

->

(int

,int

,int

)todate n =

let m = f n monthdays --- 多少個滿月

days = sum $ take m monthdays ------用掉了多少天

in(m `

div`12+

1, m `mod`12+

1, n - days +1)

where

f d mon | d < head mon =

0| otherwise =

1+ f (d - head mon)

(tail mon)

---某個日期過了 n 天後的日期

dateadd :: (

int,

int,

int)

->

int-

>

(int

,int

,int

)dateadd (y,m,d) n = todate $ todays (y,m,d)

+ n

這個新版本的 todays 用了月份天數的無限列表,舒服多了。

而且,todate 這個逆問題的解決也容易很多。

haskell 基礎題解(06)

題目 如果乙個數的所有真因子 不包含它自身的因子 之和恰等於其自身,則該數為完全數,也稱為完美數 perfect number 完全數有許多奇妙的性質。但它們很稀少,你來求前幾個吧。最小的乙個是 6,因為 6 1 2 3 這個完全數的定義已經很清楚了,如果沒有什麼妙法,就地毯式搜尋也可以。下法就是 ...

haskell 基礎題解(07)

題目 11 1 1 2 1 1 3 3 1 1 4 6 4 1 這個陣勢叫楊輝三角,國外叫帕斯卡三角。前一行的數字中,每兩個相鄰的數字相加就得到下一行的數字。左右兩邊的數永遠是 1 寫個程式,輸出前幾行的楊輝三角。import data.list intersperse yang hui int y...

haskell基礎題解(14)

題目 用自然數蛇形填充乙個 n 階的方陣。當n 5時,形如 這個問題用 haskell 解決時與 題目13 差別甚微。實際上,從函式式的思考習慣看,只要讓有些行作成後反轉一下就可以了。上 ju n f x x 0.n 1 where f row even row take n row n 1.odd...