5

I want to write a function that takes as parameters two lists and checks if every element in the first one is included in the second ( the order of the elements doesn't matter). The function will also be checking if the two list have the same length (the two list can't have duplicate elements) because if not then the function will return nill/false.

For example: (A B C D E F) and (B E A F D C) have the same elements (nil) and (nil) have the same elements

(A B C D E F) and (A B C D E F G) don't have the same elements

The problem is that I know only some basic commands and I can use just those ones. These are almost all the commands that I know:

CAR, CDR, LENGTH, NULL, MEMBER, NOT, AND, OR, NOT, MAPCAR, APPLY, DO, SETQ, LET

I wrote the following function up until now, but I don't know how to check for duplicate members and it doesn't work properly for all the lists that I want to check:

(defun same-elem-p (lst1 lst2)
  (cond ((not (null lst1))
         (cond ((member (car lst1) lst2)
                (same-elem-p (cdr lst1) lst2))
               (t nil)))
        (t t))) 

I hope I explained the problem well enough.

Kira
  • 1,153
  • 4
  • 28
  • 63
seby598
  • 141
  • 4
  • 11

7 Answers7

5

You can define a function that returns true when a list contains another one :

(defun member (x liste) 
   (cond
      ((null liste) ()) 
      ((equal (car liste) x) liste) 
      (t (member x (cdr liste))))) 

(defun inclus (liste1 liste2) 
   (cond 
      ((null liste1) t) 
      ((member (car liste1) liste2)(inclus (cdr liste1) liste2)) 
      (t ()))) 

Then use it to determine whether the two lists are equal :

(defun compare (liste1 liste2)
   (if ((and (inclus liste1 liste2) (inclus liste2 liste1)))
      (print "the 2 lists are equivalent")
      (print "the 2 lists aren't equivalent"))) 
Kira
  • 1,153
  • 4
  • 28
  • 63
  • 1
    Thanks for your answer. Would it be possible to write all this in just one function ? I was wondering why did you wrote the member function; it doesn't already exist in LISP ? – seby598 Apr 02 '13 at 18:26
  • Of course there is a way, but I don't think it would be simple! and yes member function already exists but I forgot that. It's been more than a year that I didn't program in LISP. – Kira Apr 03 '13 at 09:18
3

Two lists contain the same elements iff they are subsets of each other.

(defun same-elements (l1 l2)
  (and (subsetp l1 l2) (subsetp l2 l1)))
2

Two lists have the same elements when each element in one list is a member of the other list, and vice-versa. Assuming you can use the every function, a quick way to test this is the following:

(defun same-elements (lst1 lst2)
  (and (= (length lst1) (length lst2))
       (every #'(lambda (x) (member x lst2))
                lst1)
       (every #'(lambda (x) (member x lst1))
                lst2)))

For instance:

CL-USER> (same-elements '(a b c) '(c a b))
T

Note that this code will not handle every circumstance. For instance, it would return T when two different elements are repeated in the two lists, as in (a b b c) and (a a b c). But, for the most part, it does its job.

Andrea S.
  • 400
  • 2
  • 5
1

Write a function which maps over list1 and for each element in list1

  1. finds it in list2. If it is not in list2, then it fails.
  2. otherwise it removes it from list2
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
1

Treating a List as a Set and if you can use more commands like:

INTERSECTION SET-DIFFERENCE EQ

You can define this function:

(defun equal-lists (list1 list2)
  (and (eq (null (intersection list1 list2)) nil)
       (null (set-difference list1 list2))))

Then if intersection is not empty set, and the difference is empty, set1 is equal to set2.

BitTickler
  • 10,905
  • 5
  • 32
  • 53
  • Depending on what your list items are, this function needs an optional specifier of the equality test to use. Here a refined version which also makes the first term in the and clause more intuitive: `(defun equal-lists (list1 list2 &key (test #'equal)) (and (not (null (intersection list1 list2 :test test))) (null (set-difference list1 list2 :test test))))` – BitTickler Oct 25 '21 at 04:03
0

If your elements are numbers or if you have a decent comparator for the elements (in this case that would be alphabetical order) you can simply use the 'sort' procedure for both the lists and then check if they are the same.

Theoretically speaking, the complexity for the whole operation would be around O(N log(N)) (because the Lisp implementation of 'sort' is very good). As for Hedi's answer the complexity would be something like O (N² / 2) (because 'member' will be called N times and each call with an average time of (N / 2)).

0
(defun same ( a b )
`(cond
(( null a )'same )
((member(car a ) b ) (same(cdr a ) b ))
(t'nosame )))

(defun entre ( )
(let(( a ) ( b ))
(princ " list a : " ) (setq a (read ))
(princ " list b : " ) (setq b (read ))
(if (= (length a ) (length b )) (same  a b ) 'nosame )))
  • Please edit with more information. Code-only and "try this" answers are discouraged, because they contain no searchable content, and don't explain why someone should "try this". – abarisone Jul 22 '16 at 06:58