问题就在这里:
(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)
摆脱外部可能有助于代码清晰let
in sieve2
,并且只拨打一次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
had a result
变量已更改为filtered
. result
描述性不够,没有帮助,我认为这种改变是一种进步;毕竟,filtered
确实包含过滤的结果candidates
列表。虽然,初始值filtered
从这个意义上来说是没有意义的,因为candidates
尚未被过滤。
更让我困扰的是构造:
(cons (car candidates)
(loop (cdr candidates) (sievehelper2 candidates n)))
目前还不是很清楚(car candidates)
是一个正在被收集的素数,并且(cdr candidates)
是部分过滤的候选列表,或者目标是在完全过滤的候选列表上找到素数。
这是一个改进版本sieve
使用显式累加器primes
保存遇到的素数。什么时候sievehelper2
返回一个空列表,我们知道filtered
列表已完全过滤掉非素数。最后,找到的素数和完全过滤的候选列表可以附加在一起并返回(但不是在反转找到的素数列表之前,因为最近找到的素数被放在前面)primes
). This 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> (sieve 50)
'(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47)