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

和自甴很熟 提交于 2019-12-01 18:31:11

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.

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

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.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!