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?
>Solution :
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 ofassoc
to obtain the value, becauseassoc
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
withassoc
, the first occurrence is obtained. If you are worried about having too many updates, you can also callremove
:(acons :score 0 (remove :score libro :key #'car))
This approach allows you to add keys without worrying about breaking existing code elsewhere.
Remarks
-
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 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 ...))
.