以下代码从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)
我希望这会让它更快。然而,当我进行更改时,函数现在进入无限循环并且永远不会返回。为什么呢?
答案 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