Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lisp - Print out () instead of nil for empty list

I have a Lisp program that's going through nested list and deleting elements that match the element passed through to the function. My issue is, if everything in one of the nested list is deleted, I need to print out () instead of NIL.

(defun del (x l &optional l0)
  (cond ((null l) (reverse l0))
    ((if (atom x) (eq x (car l)) (remove (car l) x)) (del x (cdr l) l0))
    (T (del x (cdr l) (cons (if (not (atom (car l))) 
                                    (del x (car l)) 
                                    (car l))
                                 l0)))))

(defun _delete(a l)
(format t "~a~%" (del a l)))

(_delete 'nest '(nest (second nest level) (third (nest) level)))

This returns

((SECOND LEVEL (THIRD NIL LEVEL))

And I need

((SECOND LEVEL (THIRD () LEVEL))

I've tried using the ~:S format but that apparently doesn't work with composite structures. I've also tried the substitute function to replace NIL, also with no results.

like image 603
Taylor LeMaster Avatar asked Mar 11 '23 17:03

Taylor LeMaster


2 Answers

Two possible solutions:

I. You can use the format directives ~:A or ~:S

(format t "~:a" '()) => ()

However, this directive works only on the top level elements of a list, i.e.

(format t "~:a" '(a b () c))

will not print (A B () C)

but (A B NIL C)

So you need to loop through the list applying the ~:A to each element recursively if it is a cons.

(defun print-parentheses (l)
  (cond ((consp l) (format t "(")
              (do ((x l (cdr x)))
                  ((null x) (format t ")" ))
                (print-parentheses (car x))
                (when (cdr x) (format t " "))))
        (t (format t "~:a" l)) ))


(print-parentheses '(a b (c () d))) => (A B (C () D))

II. Create a print-dispatch function for empty lists and add it to the pretty print dispatch table:

(defun print-null (stream obj)
  (format stream "()") )

(set-pprint-dispatch 'null #'print-null)

(print '(a () b)) => (A () B) 

The latter is simpler, but it affects all the environment, which might not be what you want.

like image 146
Leo Avatar answered Mar 21 '23 06:03

Leo


We can write an :around method for print-object, for the case when the object to be printed is NIL.

(defvar *PRINT-NIL-AS-PARENS* nil
  "When T, NIL will print as ().")

(defmethod print-object :around ((object (eql nil)) stream)
  (if *print-nil-as-parens*
      (write-string "()" stream)
    (call-next-method)))

(defun write-with-nil-as-parens (list)
  (let ((*print-nil-as-parens* t))
    (write list)))

Example:

CL-USER 73 > (write-with-nil-as-parens '(a b c nil (()) (nil)))
(A B C () (()) (()))                  ; <- printed
(A B C NIL (NIL) (NIL))               ; <- return value
like image 39
Rainer Joswig Avatar answered Mar 21 '23 08:03

Rainer Joswig