方案素数

时间:2012-12-09 19:44:06

标签: scheme primes

这可能是一个基本问题,但我在使用Scheme编写的程序时遇到问题。该过程应返回小于或等于N的所有素数(N来自输入)。

(define (isPrimeHelper x k)
  (if (= x k) #t
  (if (= (remainder x k) 0) #f
      (isPrimeHelper x (+ k 1)))))

(define ( isPrime x )
    (cond
      (( = x 1 ) #t)
      (( = x 2 ) #t)
      ( else (isPrimeHelper x 2 ) )))

(define (printPrimesUpTo n)
    (define result '())
    (define (helper x)
        (if (= x (+ 1 n)) result
        (if (isPrime x) (cons x result) ))
        ( helper (+ x 1)))
    ( helper 1 ))

我对素数的检查工作,但函数printPrimesUpTo似乎永远循环。基本上,我们的想法是检查数字是否为素数并将其放入结果列表中。

谢谢:)

5 个答案:

答案 0 :(得分:3)

你有几件事情错了,你的代码非常不恰当。首先,数字1不是素数;事实上,它既不是素数也不是复合素。其次,result变量没有按照您的想法进行。第三,你if的使用在它出现的每个地方都是不正确的; if是一个表达式,而不是其他一些编程语言中的语句。并且,作为一种风格问题,结束括号被堆叠在线的末尾,并且不占据他们自己的线。您需要与您的教授或助教交谈,以澄清对Scheme的一些基本误解。

找到小于 n 的素数的最佳算法是Eratosthenes的筛子,大约二十二世纪前由一位发明闰日和经纬系统的希腊数学家发明的,准确地测量了地球的周长和从地球到太阳的距离,并且是亚历山大的托勒密图书馆的首席图书管理员。这是他的算法的简单版本:

(define (primes n)
  (let ((bits (make-vector (+ n 1) #t)))
    (let loop ((p 2) (ps '()))
      (cond ((< n p) (reverse ps))
            ((vector-ref bits p)
              (do ((i (+ p p) (+ i p))) ((< n i))
                (vector-set! bits i #f))
              (loop (+ p 1) (cons p ps)))
            (else (loop (+ p 1) ps))))))

称为(primes 50),返回列表(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47)。正如你试图做的那样,它比通过试验部门测试数字的素数要快得多。如果必须,这是一个正确的素性检查器:

(define (prime? n)
  (let loop ((d 2))
    (cond ((< n (* d d)) #t)
          ((zero? (modulo n d)) #f)
          (else (loop (+ d 1))))))

两种算法均可实现改进。如果您有兴趣,我会在我的博客上谦虚地推荐这个essay

答案 1 :(得分:2)

(if)函数中的(helper)表达式不是函数的尾部表达式,因此不会返回,但控件将始终继续(helper (+ x 1))并递归。

答案 2 :(得分:2)

首先,通过缩进来表达嵌套结构是一种很好的风格,因此它是在视觉上明显; 并且还要放置每个if的子句,结果< / em>和替代,在其自己的行上:

(define (isPrimeHelper x k)
  (if (= x k) 
      #t                           ; consequent
      (if (= (remainder x k) 0)    ; alternative
  ;; ^^ indentation
          #f                               ; consequent
          (isPrimeHelper x (+ k 1)))))     ; alternative

(define (printPrimesUpTo n)
    (define result '())
    (define (helper x)
        (if (= x (+ 1 n)) 
            result                  ; consequent
            (if (isPrime x)         ; alternative
                (cons x result) ))         ; no alternative!
        ;; ^^ indentation
        ( helper (+ x 1)))
    ( helper 1 ))

现在可以清楚地看到,helper函数执行的最后一件事就是始终使用递增的x值来调用自身。没有停止条件,即这是一个无限循环。

另一件事是,调用(cons x result)不会以任何方式改变result的值。为此,您需要设置它,如下所示:(set! result (cons x result))。您还需要将此表达式放在begin组中,因为它的评估不是因为它的值,而是评估它的side-effect

    (define (helper x)
        (if (= x (+ 1 n)) 
            result        
            (begin 
                (if (isPrime x) 
                    (set! result (cons x result)) )   ; no alternative!
                (helper (+ x 1)) )))

通常,明确使用set!被认为是不好的风格。表达循环的一种标准方法是使用命名为let tail-recursive代码,通常使用规范名称“loop”(但它可以是任何名称):

(define (primesUpTo n) 
  (let loop ((x n) 
             (result '())) 
    (cond 
      ((<= x 1) result)      ; return the result
      ((isPrime x) 
            (loop (- x 1) (cons x result)))   ; alter the result being built
      (else (loop (- x 1) result)))))         ; go on with the same result

,在tail-call optimization存在的情况下,实际上等同于之前的版本。

答案 3 :(得分:1)

你可以做得更好。我重新格式化了代码:

(define (prime? x)
  (define (prime-helper x k)
    (cond ((= x k) #t)
          ((= (remainder x k) 0) #f)
          (else
           (prime-helper x (+ k 1)))))
  (cond ((= x 1) #f)
        ((= x 2) #t)
         (else
          (prime-helper x 2))))

(define (primes-up-to n)
  (define (helper x)
    (cond ((= x 0) '())
          ((prime? x)
           (cons x (helper (- x 1))))
          (else
           (helper (- x 1)))))
  (reverse
    (helper n)))

scheme@(guile-user)> (primes-up-to 20)
$1 = (2 3 5 7 11 13 17 19)

请不要编写像C或Java这样的Scheme - 为了便于阅读,请查看这些style rules语言的lisp-family:不要使用驼峰式,不要在括号上加上括号自己的行,用?标记谓词,注意正确的缩进,不要在括号内添加额外的空格。

答案 4 :(得分:1)

效率更高prime?(来自Sedgewick&#34;算法&#34;):

(define (prime? n)
  (define (F n i) "helper"
    (cond ((< n (* i i)) #t)
          ((zero? (remainder n i)) #f)
          (else
           (F n (+ i 1)))))
 "primality test"
 (cond ((< n 2) #f)
     (else
      (F n 2))))