I have not found any examples of how to replace the nth
element of a list without first adding every element (one-by-one
You need to use setcar and nthcdr:
(setcar (nthcdr 17 the-list) my-variable)
The more common-lisp-like approach is to use Generalized Variables:
(setf (nth 17 the-list) my-variable)
Adding a late answer that's specifically about add-to-ordered-list
, because I think there's confusion about what it does and how it works.
add-to-ordered-list
lets you specify (record) a specific order for an individual list element.
But the list can also have elements that have no specific order recorded for them. These elements are always put at the end of the list, after all of the elements that do have specific recorded orders.
If you want to replace an element that has a recorded order then you need to remove it from the list and add its replacement (which is what you guessed).
(add-to-ordered-list 'the-list 'a 1) ; Records order 1 for element (symbol) a.
(delq a the-list) ; Removes a from the list.
(add-to-ordered-list 'the-list 'HELLO 1) ; Records order 1 for element (symbol) HELLO.
Some things to add, to help clarify a bit:
You can't use a string element, such as "HELLO"
, because recording orders for elements requires them to be distinguishable using eq
(not equal
or string=
).
You can remove the recorded order from an element, instead of removing the element from the list. If you do that, the element remains in the list, but moves after any elements that have recorded orders. That's not what you wanted here (you wanted to replace it), but know that it's possible. You do this by calling add-to-ordered-list
with the element and with no ORDERED
value (or a nil
value).
Orders recorded for elements need not be specified for all elements (see above), and they need not be consecutive. All that matters is that they can be compared numerically, so their elements can be ordered. For example, you can add elements a
, b
, and c' to an empty list, recording orders of 13, 42, and 6 for them, respectively. The result is the list
(c a b)`, because 6 < 12 < 42.
You can even record the same order for more than one list element, in which case those elements are consecutive in the list but the order among them is unimportant. You can think of the recorded orders as buckets or scores. All elements with the score 3 appear in the list before all elements with the score 7, and elements with score 3 (or 7) are consecutive.
See also this emacs.SE question.
Emacs has a concept of Generalized Variables, which essentially lets you change arbitrary storage places with setf
. For instance, you can change car
and cdr
places of list, including the car
returned by nth
. Hence, the following code
(let ((foo 10)
(l '(1 2 3 4 5 6 7 8)))
(setf (nth 4 l) foo)
l)
will return the list (1 2 3 4 10 6 7 8)
.
To put variables directly into a quoted list, you must change the kind of quoting. The standard quote ' inhibits any evaluation inside the quoted expression.
The special Backquote however lets you evaluate expressions inside a quoted expression:
(let* ((foo 10)
(l `(1 2 3 4 5 ,foo 7 foo)))
l)
The leading comma marks the subsequent expression for evaluation, whereas expressions without a leading comma will be taken literally. In the above example, the first occurrence of foo
in the list will be evaluated, and hence replace with 10
, whereas the second occurrence ends up literally in the list.