Pushnew without seat support. Is this considered a destructive macro?

One function and one macro

;; 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) 

I do not know which of the following is true:

  • cons-if-member is a non-destructive function, and pushnew-no-bells is a destructive macro.

  • Both are non-destructive.

  • cons-if-member is a non-destructive function, and the adjectives "destructive" and "non-destructive" do not apply to macros.

  • none of the above

I have no idea that pushnew is considered destructive or not, but I wanted to make everything simple by discarding the support of the place in the first place.

0
source share
2 answers

cons-if-member not destructive. It is also roughly equivalent to ADJOIN . pushnew-no-bells changes the location, but does not change the structure of the list on which the item can be clicked. However, it can change the structure of other lists, since you can use it like (let ((list (list 1 2 3 4))) (pushnew-no-bells '1 (cddr list))) . (In addition, the list form will be evaluated twice, which is not very good (but it is not the main point of this question / answer).) This is destructive in the sense that it changes this place, but it is not destructive in meaning, for example, nreverse ( nreverse can change the entire structure of the list of opponents).

Hyperspec does not make the same distinction between destructive and non-destructive. The specification for ADJOIN, for example, just says what it does

Checks if an item matches an existing list item. If the item is not an existing item, adjoin adds it to the list (as if cons) and returns the resulting list; otherwise nothing is added and the original list is returned.

and omits any mention of side effects. The documentation for PUSHNEW , on the other hand, mentions in its syntax section that this requires space

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

and in decription it is mentioned that it has side effects :.

The new list is saved in place. [emphasis added] ...

Side effects:
The contents of the place are subject to change.

While destructive and non-destructive ones capture some general ideas about how things are implemented, actual implementations are usually a little more subtle, as the programmer is concerned with what can be destructively changed and what state can be changed.

The approach that you use (that is, performing a functional implementation of some operation, and then implementing a modifying macro on top of it), however, is very good, as it will help you document which functions and macros will have side effects. This will help anyone who reads this documentation to understand what are the intended side effects of the macro (just figure out what the function will calculate and then save it back in place). If you do most of this (in fact, if you do), you should also take a good look at DEFINE-MODIFY-MACRO , which simplifies the implementation of these types of pairs of functions / macros and helps to avoid common mistakes (e.g. double rating list above )

+2
source

pushnew changes the value of its place-form (2nd argument), therefore it is destructive: it changes something "in place", and not just creates a new object (which can share the structure with an existing one). Your cons-if-member (which is better called cons-unless-member or cons-if-not-member ) does not change anything "in place", so it does not actually destroy it.

Please note, BTW., That you really cannot rule out โ€œcommon placeโ€ support due to the presence of character macros. Note:

 (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))) 
+3
source

All Articles