Lisp-打印输出()而不是空列表的nil
我有一个Lisp程序,它遍历嵌套列表并删除与传递给函数的元素相匹配的元素。我的问题是,如果其中一个嵌套列表中的所有内容都被删除,我需要打印()而不是NILLisp-打印输出()而不是空列表的nil,lisp,common-lisp,Lisp,Common Lisp,我有一个Lisp程序,它遍历嵌套列表并删除与传递给函数的元素相匹配的元素。我的问题是,如果其中一个嵌套列表中的所有内容都被删除,我需要打印()而不是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 (
(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,也没有结果 两种可能的解决方案: I.您可以使用格式指令
~:A
或~:S
(format t "~:a" '()) => ()
但是,该指令仅适用于列表的顶级元素,即
(format t "~:a" '(a b () c))
不会打印(A B()C)
但是(A B NIL C)
因此,如果元素是cons,则需要循环遍历列表,将~: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))
二,。为空列表创建打印派送函数,并将其添加到“打印派送表”中:
(defun print-null (stream obj)
(format stream "()") )
(set-pprint-dispatch 'null #'print-null)
(print '(a () b)) => (A () B)
后者更简单,但它会影响所有环境,这可能不是您想要的。对于要打印的对象为
NIL
的情况,我们可以为打印对象编写一个:around
方法
(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
CL-USER 73>(以nil作为参数写入(abc nil(())(nil)))
(A、B、C()(())(());
我还尝试了替换函数来替换NIL,也没有结果
所有标准替换函数都不起作用substitute
是一个序列处理函数:它不会递归到树结构中
sublis
和subst
函数将处理树状结构,但它们平等地对待conses的car
和cdr
字段:如果我们用替换整个树状结构中的nil
:无论什么,这适用于所有终止原子,因此(a nil b)
变为(a:whatever b:whatever)
我们必须制定类似于subst
的功能,但只影响汽车
-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:
如果我们很好地打印它,字符串只是作为数据打印,而不是作为机器可读的字符串文本打印:
[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
可以表示除()
之外的对象的唯一方法是,如果我们在一个包中,而该包没有从common lisp
包导入nil
符号(并且nil
作为本地符号插入该包中,与cl:nil
完全无关):
现在让我们看看这个包中的nil
是否是()
:
哎呀!这不再是标准的nil
;它没有特殊的行为,它对自己进行评估。我们必须引用它:
FOO[6]> (cl:eq 'nil ())
COMMON-LISP:NIL
不,不是()
对象。注意cl:eq
函数的返回值如何打印为COMMON-LISP:NIL
或COMMON-LISP:T
。只有当符号出现在当前包中时,才会在不带包前缀的情况下打印符号。要使pprint分派生效,*print pretty*
需要为T。在这种情况下,我倾向于专门处理类null
。我想这只是风格上的差异,但是专门研究(eql nil)
有什么技术原因吗?可移植性?@coredump:只是风格而已。甚至没有比较过性能。我倾向于将NIL视为一个对象,而不是一个类的实例,它代表一个类型。谢谢,我理解。请参阅的第19条-定义此方法的后果是未定义的。我认为pprint分派是一种方式。@Xach:在这种情况下,我不会太在意编写一个方法,尽管它是一种黑客行为。我要添加的唯一安全检查是,看看该方法是否已经存在。。。pprint调度非常好,但是您正在打印列表,这可能是您想要的,也可能不是您想要的。要摆脱漂亮的打印(修改了分派表)并仍然使用pprint分派是很困难的。例如,我找不到如何创建空的调度表。请问您为什么需要这样做?
[1]> (defpackage "FOO" (:use))
#<PACKAGE FOO>
[2]> (in-package "FOO")
#<PACKAGE FOO>
FOO[3]> (cl:eq cl:nil ())
COMMON-LISP:T
FOO[4]> (cl:eq nil ())
*** - SYSTEM::READ-EVAL-PRINT: variable NIL has no value
FOO[6]> (cl:eq 'nil ())
COMMON-LISP:NIL