Context
- With functions like
(lambda (List arg1 arg2 ... argn))
I can usefuncall
/apply
to call those methods with the original arguments and thus modify the list inside the lambda. - With functions like
(lambda (arg1 arg2 ... argn &key List))
I can only usefuncall
/apply
with copies of the arguments, which means I cannot modify them inside the functions. - How can I use functions like in 2. with the same functionality of those in 1.?
Issue in detail
1. Functions that work
With (lambda (mem arg1 arg2 ... argn))
:
;; Can pass the original lists for modification inside the function:
(funcall #'fn program-memory args-list)
functions can modify those lists.
2. Functions that lose ability to modify arguments
With (lambda (arg1 arg2 ... argn &key mem))
, I can only call it with a copy of the original lists:
;; can only pass copies of the lists :(
(apply #'fn (concatenate 'list args (list :mem program-memory)))
Thus I can no longer modify the program memory.
3. How can I make functions in 2. work like in 1.?
How can I make it work? I.e., call the function with the original list and not a copy.
Example with simplified old code (like in 1.):
(defun mem/w (memory address value)
"Writes the value to memory at address. Returns nil."
(setf (elt memory address) value)
nil)
;; sum
(defun sum-op (mem a b o)
(mem/w mem o (+ a b)))
(let ((program (list 1 2 3 4 5 6 7 8))
(args (list 1 2 0)))
(apply #'sum-op
(cons program args))
(print program)) ;; shows modification --> good
Full code is found at https://github.com/AlbertoEAF/advent_of_code_2019/blob/master/common-lisp/day5.lisp.
There seem to be a misunderstanding about what happens when you call:
The argument list
args
and(list :mem program-memory)
are used to build a new list. Here you could have usedappend
, like so:(append args (list :mem program-memory)
. In both cases the original lists are unmodified, but you get a fresh list of arguments (that may share the last list, but this is a detail).However, the content of both the inputs lists and the resulting list are identical, the exact same objects are referenced in those lists before and after concatenation, there is no implicit copy of objects.
Let's see:
When I evaluate
*something*
, the resulting object is printed as#<SOMETHING {10091B1973}>
(the printed representation may vary across implemetations; the identity of your object would be different).If I put it in a list, and call
copy-list
, the resulting list still holds the same value:The same applies if you store lists inside the list, they are not going to be copied recursively without an explicit call to copy. In fact let's try the same example you gave, but with keywords:
The resulting memory state is:
NB. What is an undefined behaviour is mutating the argument list itself.
Edit:
Maybe you did concatenate the memory itself with the args, in which case a new list representing the memory was used in the called function, but if so, this is an error because the concatenation is only supposed to modify the list of arguments, not one of the argument.