Lisp - 为空列表打印out()而不是nil

时间:2016-11-07 04:34:55

标签: lisp common-lisp

我有一个Lisp程序,它通过嵌套列表并删除与传递给该函数的元素匹配的元素。我的问题是,如果其中一个嵌套列表中的所有内容都被删除,我需要打印out()而不是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)))

返回

((SECOND LEVEL (THIRD NIL LEVEL))

我需要

((SECOND LEVEL (THIRD () LEVEL))

我尝试使用〜:S格式,但显然不适用于复合结构。我也尝试过替换函数替换NIL,也没有结果。

3 个答案:

答案 0 :(得分:3)

两种可能的解决方案:

予。您可以使用格式指令~:A~:S

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

但是,该指令仅适用于列表的顶级元素,即

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

不会打印(A B () C)

但是(A B NIL C)

所以你需要循环遍历列表,如果它是一个缺点,则递归地将~:A应用于每个元素。

(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。为空列表创建打印调度函数,并将其添加到漂亮的打印调度表中:

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

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

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

后者更简单,但它会影响所有环境,这可能不是您想要的。

答案 1 :(得分:3)

我们可以为:around编写一个print-object方法,用于打印对象为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)))

示例:

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

答案 2 :(得分:0)

  

我也尝试过替换函数来替换NIL,也没有结果。

标准替换功能都不起作用。 substitute是一个序列处理函数:它不会递归到树结构中。

sublissubst函数将处理树结构,但它们同等地处理carcdr个字段:如果我们替换nil整个具有:whatever的树结构,适用于所有终止原子,因此(a nil b)变为(a :whatever b . :whatever)

我们必须使我们的out函数像subst一样,但只影响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))))

有了这个,我们可以用nil字符串替换"()" - s:

[1]> (subcar nil "()" '(a b c nil (e nil f (g nil)) nil))
(A B C "()" (E "()" F (G "()")) "()")

如果我们打印出来,那么字符串只是作为数据打印而不是机器可读的字符串文字:

[2]> (format t "~a~%" *)  ;; * in the REPL refers to result of previous evaluation
(A B C () (E () F (G ())) ())

我希望您理解nil()的意思完全相同;它们是同一个对象:

[3]> (eq nil ())
T

符号标记 nil唯一可以表示()以外的对象的方法是,如果我们在一个未导入nil的包中} common-lisp包中的符号(nil作为该包中的本地符号实现,与cl:nil完全无关):

[1]> (defpackage "FOO" (:use))
#<PACKAGE FOO>
[2]> (in-package "FOO")
#<PACKAGE FOO>

完整性测试:从包foo中检查cl:nil是否与()对象相同。我们必须将eq函数称为cl:eq,因为包foo不会从cl导入任何内容:

FOO[3]> (cl:eq cl:nil ())
COMMON-LISP:T

现在让我们看看这个包中的nil()

FOO[4]> (cl:eq nil ())

*** - SYSTEM::READ-EVAL-PRINT: variable NIL has no value

OOPS!这不再是标准的nil;它没有特殊的评估它自己的行为。我们必须引用它:

FOO[6]> (cl:eq 'nil ())
COMMON-LISP:NIL

不,不是()对象。请注意cl:eq函数的返回值如何打印为COMMON-LISP:NILCOMMON-LISP:T。仅当符号存在于当前包中时,才会打印没有包前缀的符号。