Lists are also Sequences.
(defun print-cons (item) (labels ((cons-string (item) (case (type-of item) (array (let ((end (length item)) (result "")) (dotimes (index end) (let ((string (cons-string (aref item index)))) (setq result (if (eql 0 index) (format nil "#(~a" string) (format nil "~a ~a" result string))))) (format nil "~a)" result))) (character (format nil "~s" item)) (cons (format nil "(~a . ~a)" (cons-string (car item)) (cons-string (cdr item)))) (string (format nil "\"~a\"" item)) (t item)))) (format t "~a~%" (cons-string item)) item))
Examples:
> (print-cons '(1 2 3)) (1 . (2 . (3 . NIL))) (1 2 3)
The 'print-cons' function is useful for debugging association lists, where print often fails to display the correct layout:
> (print-cons (cons '((1 . 2) (3 . 4)) '((a . b) (c . d)))) (((1 . 2) . ((3 . 4) . NIL)) . ((A . B) . ((C . D) . NIL))) (((1 . 2) (3 . 4)) (A . B) (C . D)) ; <- output of PRINT
Do not think that print is bad, it saves you from reading things like this:
> (print-cons '(defun hello-world () (print "Hello World!"))) (DEFUN . (HELLO-WORLD . (NIL . ((PRINT . ("Hello World!" . NIL)) . NIL)))) (DEFUN HELLO-WORLD NIL (PRINT "Hello World!")) ; <- output of PRINT
Test this if you don't believe:
> (DEFUN . (HELLO-WORLD . (NIL . ((PRINT . ("Hello World!" . NIL)) . NIL)))) HELLO-WORLD > (hello-world) "Hello World!"
A dolist version that can iterate dotted lists:
(defmacro dolist* (fargs &rest body) (let ((list (gensym))) `(let ((,list ,(second fargs))) (if (not (listp ,list)) (error "not a list" ,list) (do ((,(first fargs) (first ,list) (if (consp ,list) (first ,list) ,list))) ((null ,list)) (setq ,list (and (consp ,list) (rest ,list))) ,@body)))))
(dolist (i '(1 2 3)) (print i)) ; prints 1 2 3 (dolist* (i '(1 2 3)) (print i)) ; prints 1 2 3 (dolist (i '(1 2 . 3)) (print i)) ; prints 1 2 (dolist* (i '(1 2 . 3)) (print i)) ; prints 1 2 3
XLISP already has the member function to for search elements in lists:
(defun cl:member (expr list &key test test-not key) (and test test-not (error "both :TEST and :TEST-NOT specified")) (if key (cond (test (member expr list :test #'(lambda (x y) (funcall test x (funcall key y))))) (test-not (member expr list :test-not #'(lambda (x y) (funcall test-not x (funcall key y))))) (t (member expr list :test #'(lambda (x y) (eql x (funcall key y)))))) (cond (test (member expr list :test test)) (test-not (member expr list :test-not test-not)) (t (member expr list)))))
Test if the number 4 matches the first or the second element in several sublists:
(cl:member 4 '((1 2) (3 4) (5 6)) :key #'first) => NIL ; no match (cl:member 4 '((1 2) (3 4) (5 6)) :key #'second) => ((3 4) (5 6)) ; number found
Subtle differences between XLISP and Common Lisp:
;; Lisp Form XLISP Common Lisp (member 1 '(1 2 . 3)) => (1 2 . 3) => (1 2 . 3) (member 2 '(1 2 . 3)) => (2 . 3) => (2 . 3) (member 3 '(1 2 . 3)) => NIL => error: not a proper list
Here is a 'cl:member' version that behaves
(defun cl:member (expr list &key test test-not key) (and test test-not (error "both :TEST and :TEST-NOT specified")) (macrolet ((internal-loop (list) `(do () ;; termination test ((or (not (consp list)) ,(if key (cond (test `(funcall ,test ,expr (funcall ,key (car list)))) (test-not `(not (funcall ,test ,expr (funcall ,key (car list))))) (t `(eql ,expr (funcall ,key (car list))))) (cond (test `(funcall ,test ,expr (car list))) (test-not `(not (funcall ,test ,expr (car list)))) (t `(eql ,expr (car list)))))) ;; return value (if (not (listp list)) (error "a proper list must not end with" list) list)) ;; body (setq list (cdr list))))) (internal-loop list)))
Here are two functions to search for elements that satisfy a given predicate:
(defun cl:member-if-not (predicate list &key key) (member nil list :test (if key #'(lambda (x y) (funcall predicate (funcall key y))) #'(lambda (x y) (funcall predicate y)))))))
(defun cl:member-if-not (predicate list &key key) (member nil list :test-not (if key #'(lambda (x y) (funcall predicate (funcall key y))) #'(lambda (x y) (funcall predicate y)))))))
Examples:
(cl:member-if #'plusp '(-2 -1 0 1 2)) => (1 2) ; 1 = first positive number (cl:member-if-not #'minusp '(-2 -1 0 1 2)) => (0 1 2) ; 0 = first non-negative number
More test functions see Predicates.
The 'lists as sets' functions have common :test,
(defmacro cl:list:accessor (test test-not &optional key) (if (and test test-not) (error "both :TEST and :TEST-NOT specified")) (if key (cond (test `(lambda (x y) (funcall ,test (funcall ,key x) (funcall ,key y)))) (test-not `(lambda (x y) (not (funcall ,test-not (funcall ,key x) (funcall ,key y))))) (t `(lambda (x y) (eql (funcall ,key x) (funcall ,key y))))) (cond (test `(lambda (x y) (funcall ,test x y))) (test-not `(lambda (x y) (not (funcall ,test-not x y)))) (t `(lambda (x y) (eql x y))))))
(defun union (a b) (let (result) (dolist (element a) (unless (member element result) (push element result))) (dolist (element b) (unless (member element result) (push element result))) result))
The 'cl:union' function returns a list that contains every element that occurs in either 'list1' or 'list2'.
(defun intersection (a b) (let (result) (dolist (element a) (when (member element b) (push element result))) result))
(defun set-difference (a b) (remove-if #'(lambda (element) (member element b)) a))
An element of list1 appears in the result if and only if it does not match any element of list2.
(set-difference '(1 2 3) '(2 3 4)) => (1)
The result contains precisely those elements of list1 and list2 that appear in no matching pair.
(set-exclusive-or '(1 2 3) '(2 3 4)) => (1 4)
(defun subsetp (a b) (let ((result t)) (dolist (element a) (when (not (member element b) (setf result nil) (return)))) result))