假设我有一个无限列表A = [1..]
我想将A
中的每个元素与列表B = [1..10]
中的所有元素分开如果列表A
中的任何元素都可被所有元素整除B
中的元素我需要打印它。
我需要继续这个,直到我得到10个这样的数字。
以下尝试不起作用:
print(minimum([x | x <- [1..], y <- [1..10], rem x y == 0]))
答案 0 :(得分:8)
您写道:
print(minimum([x | x <- [1..], y <- [1..10], rem x y == 0]))
现在由于以下几个原因,这不会起作用:
x
中的{em>任何元素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个元素。
我们可以通过计算b
中所有元素的最小公倍数(lcm)来加速最后一个,并检查一个数字是否可以被lcm分割:< / p>
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秒。但当然这只有假设才有效。
答案 1 :(得分:1)
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]