0

リストを繰り返し処理し、要素を使用してアクションを実行し、いくつかの基準に基づいて、アクティブな要素を削除したいと思います。ただし、以下の関数を使用すると、無限ループに陥ります。

 (defun foo (list action test)
   (do ((elt (car list) (car list)))
       ((null list))
     (funcall action elt)
     (when (funcall test elt)
       (delete elt list))))

(setq list '(1 2 3 4))
(foo list #'pprint #'oddp)
-> infinite loop

それ自体を指しているのでそれは不可能ですか?結局のところ、もちろんですelt(car list)

これは正しい評価ですか?そして、どうすればこれを効率的に解決できますか?

4

3 に答える 3

3

何も繰り返さないため、ループは無限になります。action繰り返し適用しますが、要素を変更しpprintない場合は、明らかにそうではないので、test結果が負の場合はそのままで、リストは変更されません。削除を試みても削除が機能した場合でも空になります。

DELETEは破壊的な機能です。Common Lispでは、破壊的な操作はそれらの引数を破壊することが許可されています。引数への参照を破棄し、戻り値のみを使用することになっています。操作が完了した後は、引数の状態についての保証はありません。特に、実装は非破壊的な対応物と同じように動作することも許可されているため、効果がない可能性がありますが、通常、シーケンスのコンポーネント部分は予測が難しい方法で再構築されます。また、例では、未定義の動作を持つリテラルを破棄しているため、回避する必要があります。

Common Lispのリストは、不変で破壊的な操作としてマイクロオプティゼーションとして扱うのが一般的に最善です。これは、リストが何も壊さないことが確実な場合にのみ使用する必要があります。この問題の場合、条件付きで結果リストをアセンブルするLOOPを使用して、リストを反復処理することができますCOLLECTPCLのLOOPの章を参照してください。

于 2012-02-27T18:08:04.460 に答える
1

実際には、リストを繰り返し処理しながら、リストの状態を変更できます。rplacdに加えてを使用しdelete、反復句ではなく、do本体内でリストに沿って進行を制御する必要があります。

 (defun nfoo (lst action test)
   (do* ((list (cons 1 lst))
         (elt (cadr list) (cadr list)))
        ((null (cdr list))
         (if (funcall test (car lst)) (cdr lst) lst))
     (funcall action elt)
     (if (funcall test elt)
       (rplacd list (delete elt (cddr list)))
       (setf list (cdr list)))))  

copy-list引数リストを破棄したくない場合は、経由で呼び出す必要があります。

eltテストに合格した要素と等しいすべての要素ではなく、テストに合格するすべての要素をリストから削除する場合は、呼び出しに引数として関数をdelete渡す必要があります。test:test

編集:)そしてこの(非破壊)バージョンのように、さらに単純でわかりやすい:

(defun foo (list action test)
   (do* ((elt (car list) (car list)))
        ((null list))
     (funcall action elt)
     (if (funcall test elt)
       (setf list (delete elt list))
       (setf list (cdr list)))))
于 2012-02-27T20:29:52.970 に答える
0

私はLispに少し慣れていないので、おそらくあなたの質問に何かが欠けています。それでも、私はあなたが何を求めているのか理解していると思います、そしてなぜあなたはこれのためにいくつかの既存の構造を使用していないのだろうremove-if-notか...remove-ifmapcar

(mapcar #'pprint (remove-if-not #'oddp '(1 2 3 4))

上記はとを出力13ます(そして戻ります(nil nil)が、おそらくそれを無視することができます...または上記を実行してで終わるdefunを行うことができます(values))。(偶数が必要な場合は、に変更remove-if-notしてremove-ifください。)

これは、教育的な理由でこれを行っているか、何かが足りない場合を除いて、おそらくより賢明な方法であると私は思います...どちらも可能です。:)

PS Hyperspec情報remove-ifremove-if-notなど

于 2012-02-28T22:11:14.873 に答える