Jacob Krieg
Jacob Krieg

Reputation: 3164

Common Lisp: Function that checks if element is member of list

I want to make a function that checks if an element is a member of a list. The list can contain other lists. This is what I came with so far:

(defun subl(l)
  (if (numberp l)
      (if (= l 10)
          (princ "Found"))
      (mapcar 'subl l)))

Now the number I am searching for is hard-coded and it is 10. I would like to write it somehow so the function takes another parameter(the number I am searching for) and returns true or 1 when it finds it. The main problem is that I can't see a way to control mapcar. mapcar executes subl on each element of l, if l si a list. But how can I controll the returned values of each call?

I would like to check the return value of each subl call and if one of it is true or 1 to return true or 1 till the last recursive call. So in the end subl returns true or one if the element is contained in the list or nil otherwise.

Any idea?

Upvotes: 0

Views: 5891

Answers (3)

sçuçu
sçuçu

Reputation: 3070

This procedure below should process as you have described;

    (defun member-nested (el l)"whether el is a member of l, el can be atom or cons,
l can be list of atoms or not"
      (cond
       ((null l) nil)
       ((equal el (car l)) t)
       ((consp (car l)) (or (member-nested el (car l))
                            (member-nested el (cdr l))))
       (t (member-nested el (cdr l)))))

Upvotes: 2

Sylwester
Sylwester

Reputation: 48745

Your function seems to play the role of main function and helper at the same time. That makes your code a lot more difficult to understand than it has to be..

So imagine you split the two:

;; a predicate to check if an element is 10
(defun number10p (l)
    (and (numberp l)
         (= l 10)))

;; the utility function to search for 10 amongst elements
(defun sublistp (haystack)
    (mapcar #'number10p haystack)))

But here when you do (sublistp '(5 10 15 20)) you'll get (nil t nil nil) back. Thats because mapcar makes a list of every result. For me it seems you are describing some since it stops at the first true value.

(defun sublistp (haystack)
    (some #'number10p haystack)))

(sublistp '(5 10 15 20)) ; ==> t

Now to make it work for any data type we change the predicate and make it as a local function where we have the argument we are searching for:

(defun sublistp (needle haystack)
  (flet ((needlep (x)
            (equal x needle)))
    (some #'needlep haystack)))

(sublistp '(a b) '(a b c (a b) d e f)) ; ==> t

You can also do this with an anonymous predicate like this:

(defun sublistp (needle haystack)
  (some #'(lambda (x)
            (equal x needle))
        haystack))

An implementation of this is the member function, except it returns the match as truth value. That's ok since anything but nil is true in CL:

(member 10 '(5 10 15 20)) ; ==> (10 15 20) 

EDIT You commented on a different answer that you are required to use mapcar in that case use it together with append to get a list of all matches and check if the list has greater than 0 elements:

(defun sublistp (needle haystack)
  (flet ((needle-check (x)
            (if (equal x needle) '(t) nil)))
    (< 0 (length 
          (apply #'append 
                 (mapcar #'needle-check haystack))))))

How it works is that for each match you get a list of one element and for every non match you get an empty list. When appending the lists you'll get the empty list when there is not match. For all other results you have a match. This is not a very efficient implementation.

Upvotes: 2

yan
yan

Reputation: 20982

mapcar is a very generic primitive to map a function over a list. You can use one of the built-in combinators which are much more closely suited with what you're trying to do. Look into the member function.

Upvotes: 2

Related Questions