3

次のコードは、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)

そして、私はこれがそれをより速くすることを望みました。しかし、私がその変更を行ったとき、関数は無限ループに入り、決して戻りません。なんで?

4

3 に答える 3

7

コメントで述べたように、変数を設定する必要があります。

DELETE-IFほとんどの場合、の破壊的なバージョンですREMOVE-IFREMOVE-IF削除された要素を含まない、新しくconsedされたシーケンスを返します。DELETE-IF再利用されるシーケンスを返す場合があります。

リストにバインドされている変数がある場合でも、結果を設定する必要があります。上記の関数は結果を返しますが、結果に変数を設定しません。リストの場合、操作の結果はDELETE-IF空のリストになる可能性があり、空でないリストを指しているときに変数をリストに設定できるという副作用はありません。

于 2012-07-26T04:27:03.763 に答える
0

CL の経験はあまりありませんが、Scheme で多くの作業を行いました。

2 番目のバージョン (sans setf a) では、remove-if 式が評価されますが、実際に a を変更することは何もしません。loop は CL のマクロであり、式を評価するだけですが、再帰関数のようにそれらの式の結果を使用しません。

そのため、最初のバージョンでは、setf によってループが実行されるたびに a の値が変更されますが、2 番目のバージョンでは、a の値は一貫して一定です。したがって、(car 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))))))))) ;

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 式が評価されていることがわかりますが、その結果は使用されていません。

于 2012-07-26T01:18:47.640 に答える
-1

クリスは正しいです。

delete-if代わりに使用することで速度を上げることができますremove-if

于 2012-07-26T01:33:51.783 に答える