Scheme 在过滤过程中使用局部状态突变筛选方案中的埃拉托什烯

Scheme 在过滤过程中使用局部状态突变筛选方案中的埃拉托什烯,scheme,racket,primes,sieve-of-eratosthenes,sieve,Scheme,Racket,Primes,Sieve Of Eratosthenes,Sieve,最近,我提出了以下代码,实现了Eratosthenes筛的一个变体,反复筛选最初的2…n序列,尽早停止: (define (sieve2 n) (let ((ls (makelist n))) (let loop ((ls ls) (next (sievehelper2 ls n))) (if (null? next) ls (cons (car ls) (loop

最近,我提出了以下代码,实现了Eratosthenes筛的一个变体,反复筛选最初的2…n序列,尽早停止:

(define (sieve2 n)
  (let ((ls (makelist n)))
    (let loop ((ls ls) 
               (next (sievehelper2 ls n)))
      (if (null? next)
          ls
          (cons (car ls) 
                (loop next (sievehelper2 ls n)))))))

(define (sievehelper2 list n)
  (if (> (* (car list) (car list)) n)
      '()
      (filterfunc (not-divisible-by (car list)) 
                  list)))

(define filterfunc filter)

(define (not-divisible-by n)
  (let ((m n))   ; the current multiple of n
    (lambda (x)
      (let ((ret (not (= x m))))
        (if (>= x m) (set! m (+ m n)) #f)
        ret))))

(define (makelist n)
  (range 2 (+ 1 n)))
在球拍中运行
(筛50)
会导致
”(23 3 5 7 11 11 13 17 19 23 29 31 37 41 43 47)

它有一些错误,这在结果中是显而易见的,我并没有立即看到它在哪里。这可能是我犯的一些愚蠢的错误,也可能是使用中的算法部分的一些基本失调的表现,我不能说哪个是哪个

请问,那个错误是什么?如何修复

明确地说,我不是要求对代码进行算法改进,我希望保留代码中表达的计算结构。此外,我在链接问题中看到的挑战是设计缺失的函数——并改变
sieve
本身——同时保持
sievehelper
函数如给定的那样,以及一些小的修改,如该问题的代码所示。这也是我想在这个问题上提出的一个要求

我也对
sieve2
中的两个调用
sievehelper2
感到不满意。也许以某种方式修复代码结构也会使错误消失?

问题在于:

(loop next (sievehelper2 ls n))
在此调用中,列表
ls
将第二次传递给
sievehelper2
;但是,
sievehelper2
需要处理
next

(define (sieve2 n)
  (let ((ls (makelist n)))
    (let loop ((ls ls) 
               (next (sievehelper2 ls n)))
      (if (null? next)
          ls
          (cons (car ls) 
                (loop next (sievehelper2 next n)))))))
有了这一变化,筛子似乎能按预期工作:

sieve2.rkt>(sieve2 50)
'(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47)
sieve2
中去掉外部的
let
,只调用一次
sievehelper2
,可能有助于代码的清晰性:

(define (sieve3 n)
  (let loop ((filtered '())
             (candidates (makelist n)))
    (if (null? candidates)
        filtered
        (cons (car candidates) 
              (loop (cdr candidates) (sievehelper2 candidates n))))))
这也如预期的那样起作用:

sieve2.rkt>(sieve3 50)
'(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47)

一些改进 我对上面的
sieve3
不满意。虽然我认为只显示一个对
sievehelper2
的调用有助于清晰,但代码仍然可以更加清晰

最初,
sieve3
有一个
结果
变量,该变量后来被更改为
已过滤
<代码>结果的描述性不足以提供帮助,我认为这一变化是一种改进;毕竟,
filtered
确实包含筛选
候选项的结果。尽管如此,
filtered
的初始值在这个意义上是没有意义的,因为
候选者
尚未被过滤

更让我困扰的是建筑:

(cons (car candidates) 
      (loop (cdr candidates) (sievehelper2 candidates n)))
尚不清楚
(car候选者)
是否是正在收集的素数,
(cdr候选者)
是否是部分筛选的候选者列表,或者目标是将已发现的素数保留在完全筛选的候选者列表中

下面是一个改进版的
sieve
,它使用显式累加器
素数
保存遇到的素数。当
sievehelper2
返回一个空列表时,我们知道
filtered
列表已完全过滤非素数。最后,可以将找到的素数和完全过滤的候选列表附加在一起并返回(但不能在反转找到的素数列表之前返回,因为最近找到的素数会显示在
素数的前面)。此
sieve
过程还具有尾部递归的优点:

(define (sieve n)
  (let loop ((primes '())
             (filtered (makelist n)))
    (let ((next (sievehelper2 filtered n)))
      (if (null? next)
          (append (reverse primes) filtered)
          (loop (cons (car filtered) primes)
                next)))))
sieve2.rkt>(50号筛)
'(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47)

您先前的评论现在已被删除,但我的回答中的
sieve3
也让我不高兴。直到现在我才有时间做任何事情;我添加了一些关于代码清晰性问题的评论,我认为这是对
sieve
过程的改进表达。我删除了我的答案,并在其中说明了原因。谢谢你的回答!