In general, lisp, how do I change part of a list parameter from a function without changing the original list?

I am trying to pass a list of a function to Lisp and change the contents of this list inside the function without affecting the original list. I read that Lisp is passing by value, and that is true, but there is something else that I don’t quite understand. For example, this code works as expected:

(defun test () (setf original '(abc)) (modify original) (print original)) (defun modify (n) (setf n '(xyz)) n) 

If you call (check), it prints (abc), although (change) returns (xyz).

However, this does not work if you are trying to change only part of the list. I suppose this has something to do with lists that have the same content the same in memory everywhere or something? Here is an example:

 (defun test () (setf original '(abc)) (modify original) (print original)) (defun modify (n) (setf (first n) 'x) n) 

Then (test) prints (xbc). So, how do I change some elements of a list parameter in a function, as if this list was local to this function?

+6
function parameter-passing pass-by-reference lisp common-lisp
source share
4 answers

SETF changes the place. n may be the place. The first element of the list n also indicates the location.

In both cases, the list held by original is passed to modify as its parameter n . This means that both original in the test function and n in the modify function now keep the same list, which means that both original and n now point to their first element.

After SETF changes n in the first case, it no longer points to this list, but to a new list. The list pointed to by original does not change. Then the new list is returned by modify , but since this value is not tied to anything, it disappears and garbage will soon be collected.

In the second case, SETF does not change n , but points to the first element of the list n . This is the same as the original list, so you can later see the modified list also through this variable.

To copy the list, use COPY-LIST .

+7
source share

Lisp lists are based on cons cells. Variables are similar to pointers to cons cells (or other Lisp objects). Changing a variable will not change other variables. Changing cons cells will be visible in all places where there are references to these cons cells.

A good book is Touretzky, General Lisp: A gentle introduction to symbolic computing .

There is also software that draws list trees and cons elements.

If you pass a list to a function like this:

 (modify (list 1 2 3)) 

Then you have three different ways to use the list:

destructive modification of cons cells

 (defun modify (list) (setf (first list) 'foo)) ; This sets the CAR of the first cons cell to 'foo . 

separation of structure

 (defun modify (list) (cons 'bar (rest list))) 

Above returns a list that shares the structure with the one passed in the list: the remaining elements in both lists are the same.

copying

 (defun modify (list) (cons 'baz (copy-list (rest list)))) 

Above, the BAZ function is similar to the BAR, but no list cells are shared as the list is copied.

Needless to say, destructive modification should often be avoided if there is no real reason for this (for example, saving memory when it's worth it).

Notes:

never destructively change literal lists of constants

Dont 'do: (let ((l' (abc))) (setf (first l) 'bar))

Reason: the list may be write protected or may be used in conjunction with other lists (organized by the compiler), etc.

also:

Enter variables

like this

 (let ((original (list 'a 'b 'c))) (setf (first original) 'bar)) 

or how is it

 (defun foo (original-list) (setf (first original-list) 'bar)) 

never SETF a undefined.

+11
source share

it is almost the same as in this example in C:

 void modify1(char *p) { p = "hi"; } void modify2(char *p) { p[0] = 'h'; } 

in both cases, a pointer is passed, if you change the pointer, you change the copy of the pointer value parameter (this is on the stack), if you change the contents, you change the value of whatever the object was specified.

+4
source share

You probably have problems, because although Lisp is passed references to objects passed by value, like in Java or Python. Your cons cells contain links that you modify, so you change both the source and local ones.

IMO, you should try to write functions in a more functional style to avoid such problems. Although Common Lisp is a multi-paradigm, a more functional style is more appropriate.

(defun modify (n) (cons' x (cdr n))

+2
source share

All Articles