I have the following structure on LISP
(setf *books* '(
(
(:tit 'Titulo1)
(:aut (quote Autor1) )
(:gen 'Genero1)
(:score 99)
)
(
(:tit 'Titulo2)
(:aut (quote Autor2) )
(:gen 'Genero2)
(:score 40)
)
))
Now, I want to reset all scores to zero, using mapcar
and lambda
. I try
(
defun reset(list)
(mapcar
(lambda(libro)
'(
(:tit (assoc :puntuacion libro))
(:aut (assoc :aut libro) )
(:gen (assoc :gen libro))
(:score 0)
)
)
list
)
)
The point is that assoc
is not being evaluated inside the lambda
function. How can I access the initial object?
You are quoting your code, so the following:
'((:tit (assoc :puntuacion libro))
(:aut (assoc :aut libro) )
(:gen (assoc :gen libro))
(:score 0))
... is not evaluated. It is a literal list that contains various symbols and numbers, including assoc
. There are two options here:
Use the backquote/comma mechanism of Lisp: the backquote is like quote except that when the quoted data contains a comma the expression after the comma is evaluated. You can imagine a template where some parts are fixed and other variables:
`((:tit ,(cdr (assoc :punctuacion libro))))
...)
Here above, the whole tree is quoted, except for the part that follows the comma. Note that you need to use cdr
on the result of assoc
to obtain the value, because assoc
returns the entire entry, in other words the (key . value)
couple.
Only shadow the part of the value that changes:
(acons :score 0 libro)
This is a purely functional approach where the :score
of zero is appended in front of the association list. The resulting structure is for example:
((:score . 0)
(:tit . 'Titulo1)
(:aut . 'Autor1 )
(:gen . 'Genero1)
(:score . 99))
The next time you ask for the :score
with assoc
, the first occurrence is obtained. If you are worried about having too many updates, you can also call remove
:
(acons :score 0 (remove :score libro :key #'car))
Note that remove
is purely functional too, a new list is built with some element removed (a destructive version would use delete
but in your case that would be wrong since your code, by quoting a list, is using literal values which are not allowed to be modified by the standard). I would advise against using destructive operations at first.
The above approach that call acons
allows you to add keys to your data structure without worrying about breaking existing code elsewhere. For example, later you can add an :iban
key without having to rewrite the code that updates :score
.
Please use readable names, like :title
or :author
Please format your code in a more conventional Lisp way
Be careful about how your data is organized:
(:score 40)
The above is an association list from :score
to the singleton list (40)
. If you write instead (acons :score 40 nil)
to build a proper association list, you will notice that this is printed as follows:
((:score . 40))
This is a list having a single entry, which is:
(:score . 40)
The above is how a cons-cell whose cdr
is not a list is printed. Generally speaking you want to call (cdr (assoc ...))
to obtain the value, but if you don't like having this dot in your data, then you have to take into account that your value, (cdr (assoc ...))
, is a list: so if you have a convention that this value is always a list of a single element, then you have to call (car (cdr (assoc ...)))
instead, also known as (cadr (assoc ...))
.