Josh Young
Josh Young

Reputation: 135

Comparing a number and a list object in lisp

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

Answers (4)

Tim Jasko
Tim Jasko

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

coredump
coredump

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

Sylwester
Sylwester

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

sds
sds

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

Related Questions