具有递归和列表理解的素数生成器

时间:2014-11-29 01:50:08

标签: haskell recursion list-comprehension primes sieve

我是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为止。

3 个答案:

答案 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])

再次展开sievez而不是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)

  1. 发出前导元素x
  2. 从尾部xs开始,让ys成为xs列表,其中删除了x的所有倍数。
  3. 要生成下一个元素,请递归调用步骤2中定义的sieve上的ys
  4. 以下是计算前几个术语的方法:

    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