Reputation: 135
I am new to lisp so I apologize if advance it this is a simple question.
I have a list:
(set ‘inventory ‘(parts
((item 1001) (shoes (color brown) (size 10) (cost 20)))
((item 2011) (skirt (color blue) (size 4) (cost 10)))
((item 2120) (pants (color white) (size 32) (cost 30)))
((item 2121) (pants (color brown) (size 34) (cost 30)))))
I am trying to write a function that looks through the list by item number and return the correct one.
(findItem 1001 inventory)
should return:
((item 1001) (shoes (color brown) (size 10) (cost 20)))
This is what I have so far:
(defun findItem (q i)
(cond
((null q)
nil)
((null (cdr i))
nil)
((eq `parts (car i))
(findItem q (cdr i)))
((eq q (cdr (car (car i))))
(car i))
(T
(findItem q (cdr i)))))
Everything seems to work except ((eq q (cdr (car (car i)))) (car i))
(cdr (car (car i)))
should return (1001) or the part number
But the eq does not evaluate to true so the function overall returns nil.
And help would be appreciated
Upvotes: 0
Views: 1004
Reputation: 1542
What you have there is an alist. You should leverage the existing functions that are built in for working with this data structure.
(defun find-item (num list)
(assoc num (cdr list)
:test (lambda (num b)
(equal num (second b)))))
Note that I have also changed the name of your function to be more in keeping with list style.
Upvotes: 0
Reputation: 38809
To extend on sds's answer, let me address some stylistic issues. Like in any other language, you should try to design your code to define a sensible interface.
I could tell you that you might prefer to use structures or objects, but I actually think that the use of lists is not necessarily bad. What bothers me is the explicit use of CADAR
or CDR
: you are making assumptions about your code which introduces coupling for no particular reason. A first step to separate abstraction from implementation is to define accessor functions, like item-reference
, row-reference
(for each entry), and so on.
If you happen to reorganize your data, you will be happy to know that you only need to change the one place where you define (item-reference x)
to be (CAADDR x)
(for example).
By the way, Common Lisp structures can be implemented on top of lists if you provide both :named
and a :type list
argument. For example, you can define an item
structure as follows:
> (defstruct (item (:type list) :named) reference)
Then, the following actually build a list where the first element indicates the type:
> (make-item :reference 300)
=> (ITEM 300)
The defstruct
also creates accessors:
> (item-reference *)
300
> (setf (item-reference **) 1010)
1010
Upvotes: 1
Reputation: 48745
(cdr (car (car '((item 1001) (shoes (color brown) (size 10) (cost 20))))))
Doesn't work since caar
is item
and you cannot take the cdr
of a symbol.
You should play with it like this before you get it right:
(defparameter *item* '((item 1001) (shoes (color brown) (size 10) (cost 20))))
(car *item*) ; ==> (item 1001)
(caar *item*) ; ==> item
(cdar *item*) ; ==> (1001)
Now c<runs of a and d>r
is an abbriviation for nested car
and cdr
eg (car (cdr (cdr x)))
(also known as third
) can be abbriviated caddr
when reading the as and ds from right to left you see why.
Numbers are not guaranteed to be eq
even when they represent the same value. Tou can use equal
to get a true value when two values look alike and there are =
which only works for numbers that might be faster in some implementations.
set
is deprecated. For making global variables use defparameter
and use *earmuffs*
to indicate that they are global variables.
I urge you to chose sensible names as q
and i
doesn't really mean anything. Also using CL coding conversion make your code concise and readable by other lispers. Eg:
(defun find-item (item-number inventory)
"Finds the record in inventory that has a specific item number"
(cond ((endp inventory) nil)
...))
Upvotes: 1
Reputation: 60014
As explained in What's the difference between eq, eql, equal, and equalp in Common Lisp?, using
eq
to compare
cons
cells is wrong.
You should either use equal
or extract
the number, i.e., replace (cdr (car (car i)))
with
cadaar
so that
you will get 1001
instead of (1001)
.
Also, set
is deprecated, use
setq
or
defparameter
instead.
Also, you can use built-in
find
instead:
(find 1001 (cdr inventory) :key #'cadar)
==> ((ITEM 1001) (SHOES (COLOR BROWN) (SIZE 10) (COST 20)))
Upvotes: 5