Question

I want to save a reference (pointer) to a part of some Data I saved in another variable:

(let ((a (list 1 2 3)))
  (let ((b (car (cdr a)))) ;here I want to set b to 2, but it is set to a copy of 2
    (setf b 4))
  a) ;evaluates to (1 2 3) instead of (1 4 2)

I could use macros, but then there would ever be much code to be executed if I want to change some Data in the middle of a list and I am not very flexible:

(defparameter *list* (create-some-list-of-arrays))
(macrolet ((a () '(nth 1000 *list*)))
  (macrolet ((b () `(aref 100 ,(a))))
    ;; I would like to change the macro a here if it were possible
    ;; but then b would mean something different
    (setf (b) "Hello")))

Is it possible, to create a variable as a reference and not as a copy?

Was it helpful?

Solution

cl-user> (let ((a '(1 2 3)))
           (let ((b (car (cdr a))))
             (setf b 4))
           a)
;Compiler warnings :
;   In an anonymous lambda form: Unused lexical variable B
(1 2 3)

A cons cell is a pair of pointers. car dereferences the first, and cdr dereferences the second. Your list is effectively

  a -> [ | ] -> [ | ] -> [ | ] -> NIL
        |        |        |
        1        2        3

Up top where you're defining b, (cdr a) gets you that second arrow. Taking the car of that dereferences the first pointer of that second cell and hands you its value. In this case, 2. If you want to change the value of that pointer, you need to setf it rather than its value.

cl-user> (let ((a '(1 2 3)))
           (let ((b (cdr a)))
             (setf (car b) 4))
           a)
(1 4 3)

OTHER TIPS

If all you need is some syntactic sugar, try symbol-macrolet:

(let ((a (list 1 2 3 4)))
  (symbol-macrolet ((b (car (cdr a))))
    (format t "~&Old: ~S~%" b)
    (setf b 'hello)
    (format t "~&New: ~S~%" b)))

Note, that this is strictly a compile-time thing. Anywhere (in the scope of the symbol-macrolet), where b is used as variable, it is expanded into (car (cdr a)) at compile time. As Sylwester already stated, there are no "references" in Common Lisp.

I wouldn't recommend this practice for general use, though.

And by the way: never change quoted data. Using (setf (car ...) ...) (and similar) on a constant list literal like '(1 2 3) will have undefined consequences.

Building on what Baggers suggested. Not exactly what you are looking for but you can define setf-expanders to create 'accessors'. So lets say your list contains information about people in the for of (first-name last-name martial-status) and when someone marries you can update it as:

(defun marital-status (person)
  (third person))

(defun (setf marital-status) (value person)
  (setf (third person) value))

(let ((person (list "John" "Doe" "Single")))
  (setf (marital-status person) "Married")
  person)
;; => ("John" "Doe" "Married")
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top