0

1 つの関数と 1 つのマクロ

;; I am sorry for the confusing function name.
;; As one answer suggests, cons-if-not-member is the better name.
(defun cons-if-member (element list)
  (if (member element list)
      list
    (cons element list)))

(defmacro pushnew-no-bells (element list)
  "pushnew without place support"
  `(setq ,list (cons-if-member ,element ,list)))

(let ((xx (list 1 2)))
  (pushnew-no-bells 0 xx)
  xx)

次のうちどれが正しいのかわかりません。

  1. cons-if-member は非破壊関数で、pushnew-no-bells は破壊マクロです。

  2. どちらも非破壊です。

  3. cons-if-member は非破壊関数であり、「破壊的」および「非破壊的」という形容詞はマクロには適用されません。

  4. 上記のどれでもない

pushnew が破壊的であると見なされるかどうかについてもわかりませんが、最初に場所のサポートを削除することで物事を単純にしたかったのです。

4

2 に答える 2

3

pushnewその場所の形式 (2 番目の引数) の値を変更するため、破壊的です。新しいオブジェクト (既存のオブジェクトと構造を共有する場合があります) を作成するだけでなく、「その場で」何かを変更します。あなたの( orcons-if-memberと呼ばれるほうがよい) は、「その場で」何も変更しないため、実際には非破壊的です。cons-unless-membercons-if-not-member

ところで、シンボルマクロが存在するため、「一般的な場所」のサポートを実際に除外することはできないことに注意してください。観察:

(defclass foo ()
  ((x :initform nil)))

(let ((instance (make-instance 'foo)))
  (with-slots (x) instance
    (pushnew-no-bells 1 x))
  (format t "~&Now: ~S~%" (slot-value instance 'x)))
于 2013-05-28T13:15:03.797 に答える
2

cons-if-member破壊的ではありません。また、ADJOINとほぼ同等です。 pushnew-no-bells場所を変更しますが、要素がプッシュされる可能性のあるリストの構造は変更しません。ただし、として使用できるため、他のリストの構造を変更できます(let ((list (list 1 2 3 4))) (pushnew-no-bells '1 (cddr list)))。(また、フォームlistは 2 回評価されますが、これは良くありません (しかし、この質問/回答の要点でもありません)。) これは、その場所を変更するという意味では破壊的ですが、次のような意味では破壊的ではありません。 、例えばnreverseis (nreverseコンスリストの構造全体を変更できる)。

Hyperspec は、破壊的と非破壊的をまったく同じように区別していません。たとえば、ADJOINの仕様は、それが何をするかを述べているだけです

item がリストの既存の要素と同じかどうかをテストします。項目が既存の要素でない場合、adjoin はそれをリストに追加し (cons によるかのように)、結果のリストを返します。それ以外の場合は、何も追加されず、元のリストが返されます。

副作用についての言及を省略しています。一方、 PUSHNEWのドキュメントでは、構文セクションで場所が必要であると述べています。

pushnew item place &key key test test-not
=> new-place-value

説明には、副作用があることが記載されています。

新しいリストが所定の位置に保存されます。[強調追加] …</p>

副作用:
場所の内容が変更される場合があります。

破壊的および非破壊的実装は、実装方法に関する一般的なアイデアをいくつか捉えていますが、実際の実装はもう少し微妙な傾向があります。プログラマは、が破壊的に変更される可能性があるか、およびどのような状態が変更される可能性があるかに関心があるためです。

ただし、使用しているアプローチ (つまり、何らかの操作の関数実装を作成し、その上に変更マクロを実装する) は非常に優れています。どの関数とマクロに副作用があるかを文書化するのに役立つからです。そのドキュメントを読んでいる人なら誰でも、マクロの意図された副作用が何であるかを理解するのに役立ちます (関数が計算するものを計算し、それを元の場所に保存するだけです)。もしあなたがこれの多くを行っているのであれば (実際、これのいずれかを行っているのであれば)、これらの種類の関数/マクロのペアを非常に簡単に実装できるようにするDEFINE-MODIFY-MACROもよく見る必要があります。一般的な落とし穴 (list上記の二重評価など) を回避するのに役立ちます。

于 2013-05-28T13:22:22.560 に答える