问题
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.
回答1:
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.
回答2:
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
回答3:
I've also tried the substitute function to replace NIL, also with no results.
None of the standard substitution functions will work. substitute
is a sequence processing function: it will not recurse into the tree structure.
The sublis
and subst
functions will process the tree structure, but they treat the car
and cdr
fields of conses equally: if we replace nil
throughout a tree structure with :whatever
, that applies to all of the terminating atoms, so that (a nil b)
becomes (a :whatever b . :whatever)
.
We must make our out function which is like subst
, but only affects car
-s:
(defun subcar (old new nested-list)
(cond
((eq nested-list old) new)
((atom nested-list) nested-list)
(t (mapcar (lambda (atom-or-sublist)
(subcar old new atom-or-sublist))
nested-list))))
With this, we can replace nil
-s with the character string "()"
:
[1]> (subcar nil "()" '(a b c nil (e nil f (g nil)) nil))
(A B C "()" (E "()" F (G "()")) "()")
If we pretty-print that, the character strings just print as the data rather than as machine-readable string literals:
[2]> (format t "~a~%" *) ;; * in the REPL refers to result of previous evaluation
(A B C () (E () F (G ())) ())
I hope you understand that nil
and ()
mean exactly the same thing; they are the same object:
[3]> (eq nil ())
T
The only way the symbol token nil
can denote an object other than ()
is if we we are in a package which hasn't imported the nil
symbol from the common-lisp
package (and nil
is interned as a local symbol in that package, completely unrelated to cl:nil
):
[1]> (defpackage "FOO" (:use))
#<PACKAGE FOO>
[2]> (in-package "FOO")
#<PACKAGE FOO>
Sanity test: from within package foo
check that cl:nil
is the same as the ()
object. We have to refer to the eq
function as cl:eq
because package foo
doesn't import anything from cl
:
FOO[3]> (cl:eq cl:nil ())
COMMON-LISP:T
Now let's see if nil
in this package is ()
:
FOO[4]> (cl:eq nil ())
*** - SYSTEM::READ-EVAL-PRINT: variable NIL has no value
OOPS! This is not the standard nil
anymore; it doesn't have special the behavior that it evaluates to itself. We must quote it:
FOO[6]> (cl:eq 'nil ())
COMMON-LISP:NIL
Nope, not the ()
object. Note how the return values of the cl:eq
function are printed as COMMON-LISP:NIL
or COMMON-LISP:T
. Symbols are printed without a package prefix only if they are present in the current package.
来源:https://stackoverflow.com/questions/40457915/lisp-print-out-instead-of-nil-for-empty-list