Haskell 用于排除埃拉托色尼筛法未按预期工作的数字的代码

Haskell code to exclude numbers for Sieve of Eratosthenes not working as expected

我带来了以下 Sieve of Eratosthenes 实现:

sieve :: (Integral a) => [a] -> [a]
sieve [] = []
sieve (p:ps) = p:[x | x <- sieve ps, (rem x p) /= 0] 

primes :: (Integral a) => [a]
primes = sieve [2..100]

gchi 调用 primes 输出:

[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]

p2 添加开始标记数字的 optimization step,我最终得到以下代码:

sieve :: (Integral a) => [a] -> [a]
sieve [] = []
sieve (p:ps) = p:[x | x <- sieve ps, x > p ^ 2, (rem x p) /= 0] 

primes :: (Integral a) => [a]
primes = sieve [2..100]

但它产生以下输出:

[2]

我是 Haskell 的新手,所以我无法理解为什么添加 x > p ^ 2 会产生这个结果。

你能解释一下 Haskell 如何计算那个表达式来发现我的错误吗?

这行代码:

p:[x | x <- sieve ps, x > p ^ 2, (rem x p) /= 0] 

说 "take all x from sieve ps such that x > p^2 and x is not divisible by p"。这意味着所有小于或等于 p^2 的数字都将被丢弃。显然是不正确的(最小反例:[2..3])。

你有

sieve [2..10]

扩展为

2 : [x1 | x1 <- sieve [3..10], x1 > 4, rem x1 2 /= 0]
    = 2 : [x1 | x1 <- 3 : [x2 | x2 <- sieve [4..10],
                                x2 > 9,
                                x2 `rem` 3 /= 0],
                x1 > 4,
                x1 `rem` 2 /= 0]

所以第一个 x13,但是 3 > 4False 所以我们转到下一个:

    = 2 : [x1 | x1 <- [x2 | x2 <- sieve [4..10],
                            x2 > 9,
                            x2 `rem` 3 /= 0],
                x1 > 4,
                x1 `rem` 2 /= 0]

    = 2 : [x1 | x1 <- [x2 | x2 <- 4 : [x3 | x3 <- sieve [5..10],
                                            x3 > 16,
                                            x3 `rem` 4 /= 0],
                            x2 > 9,
                            x2 `rem` 3 /= 0],
                x1 > 4,
                x1 `rem` 2 /= 0]

所以如果 x24x2 > 9 是假的,所以我们移动到下一个元素:

    = 2 : [x1 | x1 <- [x2 | x2 <- [x3 | x3 <- sieve [5..10],
                                        x3 > 16,
                                        x3 `rem` 4 /= 0],
                            x2 > 9,
                            x2 `rem` 3 /= 0],
                x1 > 4,
                x1 `rem` 2 /= 0]

所以我们已经可以看到,我们知道返回的唯一实际值是 23 被跳过,因为 3 > 4False4 被跳过,但由于错误的原因,5 将被跳过,因为 5 > 16False,等等。这里的问题是你的条件 x > p ^ 2 过滤了整个列表,但你真的想在你的列表中跳到前面。这意味着您真正感兴趣的值正在从输出中过滤掉。