Common Lisp:是删除 - 如果与setf + remove-if相同?

时间:2012-07-26 00:43:15

标签: lisp common-lisp primes

以下代码从1到n生成素数:

(defun prime-list(n)
  (let ((a)(b)(x (floor (sqrt n))))
    (loop for i from (floor n 6) downto 1 do
          (push (1+ (* 6 i)) a)
          (push (1- (* 6 i)) a))
    (loop while (<= (car a) x) do
          (push (car a) b)
          (setf a (remove-if #'(lambda(m)(or (= 0 (mod m (car a))) (> m n))) a)))
    (append '(2 3) (reverse b) a)))

在我看来这部分

(setf a (remove-if #'XXX a)) 

可以替换为

(delete-if #'XXX a)

我希望这会让它更快。然而,当我进行更改时,函数现在进入无限循环并且永远不会返回。为什么呢?

3 个答案:

答案 0 :(得分:7)

如评论中所述,您需要设置变量。

DELETE-IF主要是REMOVE-IF的破坏性版本。 REMOVE-IF返回一个新的序列序列,该序列不包含已删除的元素。 DELETE-IF可能会返回一个重复使用的序列。

如果您有一个绑定到列表的变量,则仍需要设置结果。上面的函数返回结果,但它们不会将变量设置为结果。在列表的情况下,DELETE-IF操作的结果可以是空列表,并且副作用不可能是,可以将变量设置为它 - 当它指向非空时列表。

答案 1 :(得分:0)

我没有很多CL经验,但我在Scheme中做了很多工作。

在第二个版本(sans setf a)中,会计算remove-if表达式,但它实际上不会更改a。循环是CL中的一个宏,它只是计算表达式,但不会像递归函数那样使用这些表达式的结果。

因此在第一个版本中,由于setf,每次循环运行时a的值都会更改,但在第二个版本中,a的值在整个过程中都是常量。因此(汽车a)永远不会改变,循环永远不会终止。

我们可以在循环语句中比较macroexpand的结果:

没有setf:

(MACROLET ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-ERROR)))
 (BLOCK NIL
  (LET NIL
   (MACROLET ((LOOP-FINISH NIL '(GO SYSTEM::END-LOOP)))
    (TAGBODY SYSTEM::BEGIN-LOOP
     (PROGN (UNLESS (< (CAR A) X) (LOOP-FINISH))
      (PROGN (PUSH (CAR A) B) (REMOVE-IF #'(LAMBDA (M) (= 0 (MOD M (CAR A)))) A)))
     (GO SYSTEM::BEGIN-LOOP) SYSTEM::END-LOOP
     (MACROLET
      ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-WARN) '(GO SYSTEM::END-LOOP))))))))) ;

with setf:

(MACROLET ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-ERROR)))
 (BLOCK NIL
  (LET NIL
   (MACROLET ((LOOP-FINISH NIL '(GO SYSTEM::END-LOOP)))
    (TAGBODY SYSTEM::BEGIN-LOOP
     (PROGN (UNLESS (< (CAR A) X) (LOOP-FINISH))
      (PROGN (PUSH (CAR A) B)
       (SETF A (REMOVE-IF #'(LAMBDA (M) (= 0 (MOD M (CAR A))))) A)))
     (GO SYSTEM::BEGIN-LOOP) SYSTEM::END-LOOP
     (MACROLET
      ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-WARN) '(GO SYSTEM::END-LOOP))))))))) ;

你可以看到在第一个循环中评估了remove-if表达式,但是没有使用它的结果。

答案 2 :(得分:-1)

克里斯是对的。

您可以使用delete-if代替remove-if

来加快速度