Nyquist / XLISP 2.0  -  Contents | Tutorials | Examples | Reference

Lists


Lists are also Sequences.


print-cons


(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!"

  Back to top


dolist*


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

  Back to top


cl:member


XLISP already has the member function to for search elements in lists:

(member expr list [{:test | :test-not} test])
expr - the expression to find [an atom or a list]
list - the list to search
test - optional test function, default is eql
returns - the remainder of the list starting with expr

The 'cl:member' function provides an additional :key argument for accessing sub-elements in the list:

(cl:member expr list [{:test | :test-not} test :key key])
expr - the expression to find [an atom or a list]
list - the list to search
test - an optional test function, default is eql
key - an optional accessor function for sub-elements in the list
returns - the remainder of the list starting with expr

(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 error-conform to Common Lisp but produces an unintelligible backtrace in case of Lisp errors. I also have found no way how to macroexpand macrolets, so debugging this function is a real pain.

(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)))

  Back to top


cl:member-if


Here are two functions to search for elements that satisfy a given predicate:

(member-if predicate list [:key key])
predicate - a test function with one argument
list - the list to search
key - optional accessor function for sub-elements in the list
returns - the remainder of the list starting with the first matching element

(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)))))))

  Back to top


cl:member-if-not


(member-if-not predicate list [:key key])
predicate - a test function with one argument
list - the list to search
key - optional accessor function for sub-elements in the list
returns - the remainder of the list starting with the first non-matching element

(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.

  Back to top


cl:list:accessor


The 'lists as sets' functions have common :test, :test-not and :key parameters:

(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))))))

  Back to top


cl:pushnew


  Back to top


cl:union


(union list1 list2 [{:test | :test-not} test :key key])
listN - a list of symbols or numbers
returns - the union of list1 and list2

(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'.

  Back to top


cl:intersection


(intersection list1 list2 [{:test | :test-not} test :key key])
listN - a list of symbols or numbers
returns - the intersection of list1 and list2

(defun intersection (a b)
  (let (result)
    (dolist (element a)
      (when (member element b)
        (push element result)))
    result))

  Back to top


cl:set-difference


(set-difference list1 list2)
listN - a list of symbols or numbers
returns - the set-difference of list1 and list2

(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)

  Back to top


cl:set-exclusive-or


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)

  Back to top


cl:subsetp


(subsetp list1 list2)
listN - a list of symbols or numbers
returns -  T  if list1 is a subset of list2, NIL otherwise

(defun subsetp (a b)
  (let ((result t))
    (dolist (element a)
      (when (not (member element b)
        (setf result nil)
        (return))))
    result))

  Back to top


Nyquist / XLISP 2.0  -  Contents | Tutorials | Examples | Reference