haskell 基礎題解(30)

2021-09-26 03:36:49 字數 2910 閱讀 8100

【問題】

我們都會手算除法,比如:123除以13。

當然,有的時候會出現無限迴圈小數。

1 請模擬手算除法的過程,計算a除以b的小數後100位。

2 發現無限迴圈小數的迴圈節,並標識出來。

比如:123除以13,表示為:

9.[461538]

這個題目要求我們模擬手算除法的過程。初看,用命令式的思考更直觀,但因為要儲存處理計算的過程,也並不佔便宜。。。。

換用 haskell 呢? 如果覺得有障礙,就是不能改變變數的值了。這可以通過傳入傳出狀態來解決。先模擬命令式的想法:

import

data

.list (elemindex)

----求真分數的十進位制小數形式,保證: n分子 < n分母

----返回值 (商的不迴圈部分,商的迴圈部分),都是逆序的

f真分數 :: integral a =

> a -

> a -

>

([a]

,[a]

)f真分數 n分子 n分母 = f [

][n分子]

where

f l商 l餘 =

case

(head l餘 *10)

`divmod` n分母 of

(t,0)-

>

(t:l商,

)(t, u)

->

case u `elemindex` l餘 of

just n -

>

(drop n l商, t:take n l商)

nothing -

> f (t:l商)

(u:l餘)

f分數 :: (

show a, integral a)

=> a -

> a -

> string

f分數 n分子 n分母 = let

(n整數部分, n餘數)

= n分子 `divmod` n分母

(l商, l商迴圈)

= f真分數 n餘數 n分母

inshow n整數部分

++"."++

(concat . map show

. reverse $ l商)++

"["+

+(concat . map show

. reverse $ l商迴圈)++

"]"main :: io (

)main =

do putstrln $ f分數 1

3 putstrln $ f分數 123

13 putstrln $ f分數 23

56

這裡,我們用 l商 l餘 分別表示商和餘數的序列,並在 f 函式中傳遞。

遞迴的出口是:要以餘數遇到了 0, 要麼餘數出現了迴圈。

之所以都用逆序,是因為 ( : ) 比 ( ++ ) 更有效率而已。

然後再把輸出安排一下就好了。

既然是函式式程式設計,就要跳出命令式的圈子。我們需要的是。。定義。。。定義。。不是步驟。。。不是步驟。。。

如上口訣念了 n 遍之後,得出一解:

import

data

.list (elemindex, splitat)

--- 把真分數表示為迴圈小數的形式

--- 1/3 = 0.3333 --> (,[3])

--- 23/56 = 0.410714285771428... ---> ([4,1,0], [7,1,4,2,8,5])

--- 1/5 = 0.200.... --> ([2],[0]) 能除盡的小數,看作以 [0] 為其小數的迴圈部分

f真分數迴圈 :: integral a =

> a -

> a -

>

([a]

,[a]

)f真分數迴圈 n分子 n分母 =

let

xs =

iterate

(\(_,u)

->

(u *10)

`divmod` n分母)(0

,n分子)

l商 = tail $ map fst xs --從小數點後記商,甩掉頭0

l餘 = map snd xs

ys = map (

`elemindex` l餘) l餘 ---元素在餘數列中首次出現位置

(n節尾, n節首)

= head [

(a,b)

|(just a,just b)

<

- ys `zip` tail ys, a>=b]

in splitat n節首 (take (n節尾+

1) l商)

ok :: (

show a, integral a)

=> a -

> a -

> string

ok n分子 n分母 = let

(n整部, n零部)

= n分子 `divmod` n分母

(xs,ys)

= f真分數迴圈 n零部 n分母

inshow n整部 +

+"."

++ concatmap show xs

++"["

++ concatmap show ys +

+"]"

main :: io (

)main =

do putstrln $ ok 1

3 putstrln $ ok 123

13 putstrln $ ok 23

56

這裡首先構造出 商 和 餘數 的無限列。

再從列中算出迴圈節的起始和結束位置。

然後就是簡單的拼接了。

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...