假設我有一個無限列表A = [1..]
我想每個元素在A
在列表B = [1..10]
所有元素劃分如果在列表A
任何元素除盡所有B
中的元素需要打印。 我需要繼續這個,直到我得到10個這樣的數字。如何的每一個元素與另一個列表中的每個元素在Haskell劃分在一個列表
下嘗試沒有工作:
print(minimum([x | x <- [1..], y <- [1..10], rem x y == 0]))
假設我有一個無限列表A = [1..]
我想每個元素在A
在列表B = [1..10]
所有元素劃分如果在列表A
任何元素除盡所有B
中的元素需要打印。 我需要繼續這個,直到我得到10個這樣的數字。如何的每一個元素與另一個列表中的每個元素在Haskell劃分在一個列表
下嘗試沒有工作:
print(minimum([x | x <- [1..], y <- [1..10], rem x y == 0]))
您寫道:
print(minimum([x | x <- [1..], y <- [1..10], rem x y == 0]))
現在,這不會有以下幾個原因工作:
x
添加到列表理解列表中,因爲有任何元素y
在[1..10]
中除以x
。此外,對於任何此類y
,我們將一次添加x
。所以給定x
是6
,它會將它添加三次四次,因爲6
可以被1,2,3和6除盡;head
;minimum
只會產生一個元素,但你要第10首先,我們可以使用列表解析生成所有這些數字(任意名單as
和bs
):
divide_all as bs = [a | a <- as, all ((0 ==) . mod a) bs]
所以這裏的名單理解迭代as
環節都分配給a
。接下來我們有一個過濾器all ((0 ==) . mod a) bs
,這是一個緊湊的形式all (\b -> mod a b == 0) bs
。因此它檢查是否b
中的所有成員bs
,mod a b == 0
(因此a
可由b
分割)。如果過濾器得到滿足,則我們將a
(在列表理解的頭部)添加到結果中。請注意,這樣的列表是懶散地構建的,因此as
具有無限數量的元素的事實不是問題。
現在我們可以使用take :: Int -> [a] -> [a]
採取這些數字的第一個10,從而打印這些:
mapM_ print (take 10 $ divide_all [1..] [1..10])
它打印:
Prelude> mapM_ print (take 10 $ divide_all [1..] [1..10])
2520
5040
7560
10080
12600
15120
17640
20160
22680
25200
上述方法效率不高:對於a
的每個元素,我們需要檢查它是否可以用b
的每個元素進行分割。我的機器用了2.16秒來計算這個列表的第1000個元素,用了10.21秒找到第5000個元素。
可加快我們的最後一個,通過計算最小公倍數(LCM)的b
所有的元素,並檢查是否一個數是由LCM可分:所以現在
divide_all as bs = [a | a <- as, mod a lcmb == 0] -- optimized version
where lcmb = foldr1 lcm bs
我們只需要執行一次支票。現在計算第1000個元素需要0.95秒,計算第5000個元素需要4.54秒。
as = [1..]
如果as
被稱爲是[1..]
我們可以顯着提升該代碼,因爲我們知道,a
的元素是lcmb
倍數。因此,我們可以放下as
參數,並使用:
divide_all bs = [lcmb*a | a <- [1..]] -- optimized version
where lcmb = foldr1 lcm bs
現在計算的第1000個元素需要0.01秒,並計算第5000元0.03秒。但當然這隻有在假設的情況下才有效。
@ Willem Van Onsem謝謝您的詳細解釋:) – VVV
let divcheck = (take 10 .) . filter . flip (all . ((0 ==) .) . mod)
divcheck [1..10] [1..]
-- [2520,5040,7560,10080,12600,15120,17640,20160,22680,25200]
divcheck [1,2,3] [1..]
-- [6,12,18,24,30,36,42,48,54,60]
你有什麼試過自己? –
@ Willem Van Onsem我寫了一個絕對不會給出正確答案的代碼! print(minimum([x | x < - [1 ..],y < - [1..10],rem x y == 0])) – VVV