【问题标题】:Print first N prime numbers in Common Lisp在 Common Lisp 中打印前 N 个素数
【发布时间】:2013-03-26 21:21:33
【问题描述】:

我正在制作一个 Common Lisp 函数来打印前 N 个素数。到目前为止,我已经设法编写了这段代码:

;globals 
(setf isprime 1) ;if 1 then its a prime, 0 if not.
(setf from 1)    ;start from 1
(setf count 0)   ;should act as counter to check if we have already 
                 ;    N primes printed

;function so far.
(defun prime-numbers (to)
  (if (> count to) nil(progn
      (is-prime from from)
      (if (= isprime 1) (print from)(setf count (+ count 1)))
      (setf isprime 1)
      (setf from (+ from 1))
      (prime-numbers to)))
  (if (>= count to)(setf count 0) (setf from 1)))  

;code to check if a number is prime    
(defun is-prime(num val)
  (if (< num 3) nil 
    (progn
      (if (= (mod val (- num 1)) 0) (setf isprime 0))
      (is-prime (- num 1) val))))

我的问题是,它不能正确打印 N 个素数。 如果我打电话给&gt;(prime-numbers 10), 结果是: 1 2 3 5 7 11 13 17 19 1, 即它只正确打印了 9 个素数。

但是如果我打电话给&gt;(prime-numbers 2) 结果是:1 2 3 5 7 1

我在这里做错了什么?这是我第一次用 LISP 编写代码。

更新:

  (defparameter from 1)
  (defparameter count 0)

  (defun prime-numbers (to)
     (if (> count to)nil
         (progn
            (when (is-prime from) 
                (print from)
                (setf count (+ count 1)))
            (setf from (+ from 1))
            (prime-numbers to)))
     (when (>= count to)
         (setf count 0) 
         (setf from 1)))  

  (defun is-prime (n)
    (cond ((= 2 n) t)
     ((= 3 n) t)
     ((evenp n) nil)
     (t 
       (loop for i from 3 to (isqrt n) by 2
             never (zerop (mod n i))))))

工作正常。但最后输出 NIL。

【问题讨论】:

  • 请修正代码格式并去掉defuns前面的反引号
  • CLISP 是编程语言“Common Lisp”的一种实现。
  • 我重新缩进了你的第一个代码,没有触及第二个。那里有一些不恰当的缩进。

标签: printing numbers lisp primes clisp


【解决方案1】:

首先,这里根本不需要使用全局变量。

使用真/假返回值。这将允许您的 is-prime 函数类似于:

(defun is-prime (n)
  (cond ((= 2 n) t) ;; Hard-code "2 is a prime"
        ((= 3 n) t) ;; Hard-code "3 is a prime"
        ((evenp n) nil) ;; If we're looking at an even now, it's not a prime
        (t ;; If it is divisible by an odd number below its square root, it's not prime
           (loop for i from 3 to (isqrt n) by 2
                 never (zerop (mod n i))))))

这样,函数不依赖任何外部状态,没有什么可以混淆任何东西。

其次,你看到的最后一个1(可能)是函数的返回值。

要检查这一点,请尝试: (progn (prime-numbers 10) nil)

第三,重写你的 prime-numbers 函数以不使用全局变量。

第四,切勿使用setfsetq 创建全局变量,请使用defvar or defparameter。在您的全局(实际上是“特殊”)变量上使用 *earmuffs* 也是(大多数情况下,但有些人不同意)很好的风格。

【讨论】:

  • 您可以在您的loop 中使用always 而不是return-from
  • @sds 是的。自从我愤怒地写循环以来已经有几年了,return-from 触手可及。
  • @sds 使用never重写。
  • 谢谢你!但我仍然没有所需的输出。除了全局变量。我的素数有什么问题?
  • @binaryjc 我实际上认为使用全局变量会导致您的问题。使用全局状态在函数之间进行通信几乎是使代码无法正常工作的最佳方法。
【解决方案2】:

要扩展 Vatines 的答案:

prime-numbers 函数的可能重写,使用相同的算法但避免全局变量是

(defun prime-numbers (num &optional (from 2))
   (cond ((<= num 0) nil)
         ((is-prime from) (cons from (prime-numbers (1- num) (1+ from))))
         (t (prime-numbers num (1+ from)))))

这个函数也返回素数而不是打印它们。

这个递归解决方案的问题是它消耗每个找到/测试的素数的堆栈。因此,对于较大的 num 值,堆栈空间可能会耗尽。

非递归变体是

(defun prime-numbers (num &optional (start 2))
   (loop for n upfrom start
         when (is-prime n)
         sum 1 into count
         and collect n
         until (>= count num)))

【讨论】:

    猜你喜欢
    • 2012-08-06
    • 2016-02-16
    • 1970-01-01
    • 2016-04-26
    • 2013-03-24
    • 1970-01-01
    • 2015-06-12
    • 2014-06-27
    • 1970-01-01
    相关资源
    最近更新 更多