我有一个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,也没有结果。
答案 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
是一个序列处理函数:它不会递归到树结构中。
sublis
和subst
函数将处理树结构,但它们同等地处理car
和cdr
个字段:如果我们替换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:NIL
或COMMON-LISP:T
。仅当符号存在于当前包中时,才会打印没有包前缀的符号。