我是Haskell编程的新手,无法理解下面的列表理解如何扩展。
primes = sieve [2..]
sieve (p:xs) = p : sieve [x | x <-xs, x `mod` p /= 0]
有人可以纠正我sieve
扩展如何运作:
sieve
中的模式匹配,p
会与2
中的x
和[3..]
相关联。x<-3
中,但是如果没有短路,我们怎样才能使用3
调用筛网。我不明白的另一件事是递归如何在这里起作用。
我认为如果能够在前几个数字的情况下一次扩展上述一个步骤就会很清楚,直到5
为止。
答案 0 :(得分:8)
让我们做一些等式推理。
primes = sieve [2..]
sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]
[2..]
是[2, 3, 4, 5, ...]
的sintactic糖,所以
primes = sieve [2, 3, 4, 5, 6, ...]
内联sieve
一次:
primes = 2 : sieve [x | x <- [3, 4, 5, 6, 7, ...], x `mod` 2 /= 0]
首先,x
获得通过3
过滤器的值mod 2
primes = 2 : sieve (3 : [x | x <- [4, 5, 6, 7, ...], x `mod` 2 /= 0])
再次内联sieve
(我将x
重命名为y
以防止混淆)
primes = 2 : 3 : sieve [y | y <- [x | x <- [4, 5, 6, 7, ...], x `mod` 2 /= 0],
y `mod` 3 /= 0]
现在x = 4
未通过mod 2
过滤器,但x = 5
通过了它。所以
primes = 2 : 3 : sieve [y | y <- 5 : [x | x <- [6, 7, 8, ...], x `mod` 2 /= 0],
y `mod` 3 /= 0]
此y = 5
也会传递mod 3
过滤器,现在我们已经
primes = 2 : 3 : sieve (5 : [y | y <- [x | x <- [6, 7, 8, ...], x `mod` 2 /= 0],
y `mod` 3 /= 0])
再次展开sieve
(z
而不是y
)让我们进入
primes = 2 : 3 : 5 : sieve [z | z <- [y | y <- [x | x <- [6, 7, 8, ...],
x `mod` 2 /= 0],
y `mod` 3 /= 0],
z `mod` 5 /= 0]
扩张继续以同样的方式进行。
答案 1 :(得分:3)
以下是sieve
所做的操作说明。
计算sieve (x:xs)
:
x
。xs
开始,让ys
成为xs
列表,其中删除了x
的所有倍数。sieve
上的ys
。以下是计算前几个术语的方法:
sieve [2..]
= sieve (2:[3..]) -- x = 2, xs = [3..]
= 2 : sieve ys
where ys = [3..] with all of the multiples of 2 removed
= [3,5,7,9,...]
= 2 : sieve [3,5,7,9,...]
然后:
sieve [3,5,7,9,...] -- x = 3, xs = [5,7,9,11,...]
= 3 : sieve ys
where ys = [5,7,9,11,13,15,17,...] with all of the multiples of 3 removed
= [5,7, 11,13, 17,...]
= 3 : sieve [5,7,11,13,17,...]
然后:
sieve [5,7,11,13,17,...] -- x = 5, xs = [7,11,13,17..]
= 5 : sieve ys
where ys = [7, 11,13, 17,19,...] with all of the multiples of 5 removed
= [7, 11,13, 17,19,...] (the first one will be 25, then 35,...)
= 5 : sieve [7,11,13,17,19,...]
等
答案 2 :(得分:3)
使用辅助功能
transform (p:xs) = [x | x <- xs, mod x p /= 0]
= filter (\x-> mod x p /= 0) xs -- remove all multiples of p
= xs >>= noMult p -- feed xs through a tester
-- where
noMult p x = [x | rem x p > 0] -- keep x if not multiple of p
我们可以将sieve
函数重写为
._________________________________________________
| |
| sieve input = |
| .___________________________ |
| | | |
<--------- head input : | sieve (transform input ) | |
| | | |
| \===========================+ |
| |
\=================================================+
在命令式伪代码中,我们可以将其写为
sieve input =
while (True) :
emit (head input)
input := transform input
这种重复应用程序模式称为 iteration :
iterate f x = loop x
where
loop x = x : loop (f x) -- [x, f x, f (f x), f (f (f x)), ...]
那样
sieve xs = map head ( iterate transform xs )
当然,每个步骤中每个转换序列的头元素都是素数,因为我们已经删除了前面步骤中所有前面素数的倍数。
Haskell是懒惰的,所以转换不会在每一步都完全完成,远离它 - 只需要根据需要完成。这意味着只生产第一个元素,并且&#34;发出通知&#34;当被问到时,进一步进行转换:
<---- 2 --- [2..]
<---- 3 --- [3..] >>= noMult 2
<---- 5 --- ([4..] >>= noMult 2) >>= noMult 3
<---- 7 --- (([6..] >>= noMult 2) >>= noMult 3) >>= noMult 5
((([8..] >>= noMult 2) >>= noMult 3) >>= noMult 5) >>= noMult 7
.......
顺便提一下,这应该给我们一个想法: 3 不需要 4..8 不需要&em; 7 ; 9..24 5 我们想要的是以下内容:
<---- 2,3 --- [2..]
<---- 5,7 --- [4..] >>= noMult 2
<---- 11,...,23 --- ([9..] >>= noMult 2) >>= noMult 3
<---- 29,...,47 --- (([25..] >>= noMult 2) >>= noMult 3) >>= noMult 5
((([49..] >>= noMult 2) >>= noMult 3) >>= noMult 5)
....... >>= noMult 7
即。我们希望创建每个>>= noMult p
过滤器postponed,直到输入中达到p*p
。