2016-11-07 30 views
3

我有一个Lisp程序正在通过嵌套列表并删除匹配传递给函数的元素的元素。我的问题是,如果一个嵌套列表中的所有内容都被删除,我需要输出()而不是NIL。Lisp - 打印出()而不是零空列表

(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

请问为什么你需要这样做? – coredump

回答

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)) 

二,创建空列表的打印调度功能,并把它添加到漂亮的打印调度表:

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

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

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

后者是简单,但它会影响所有的环境中,你想要什么这可能不是。

+2

对于pprint dispatch的工作,'* print-pretty *'需要是T. –

+0

这很好。谢谢! –

3

对于要打印的对象是NIL的情况,我们可以编写:around方法print-object

(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 
+0

在这种情况下,我倾向于专注于类'null'。我想这只是一种风格差异,但是有没有专门研究'(eql nil)'的技术原因?可移植性? – coredump

+2

@coredump:只是风格。甚至没有比较表现。我倾向于将NIL看作一个对象,而不是一个类的实例,它代表了一种类型。 –

+0

谢谢,我明白了。 – coredump

0

我也尝试替代功能没有结果来代替NIL,也。

没有标准的替代函数可以使用。 substitute是一个序列处理函数:它不会递归到树结构中。

sublissubst功能将处理树状结构,但他们对待conses之外的carcdr领域同样:如果我们在整个树形结构:whatever,适用于所有终端原子的取代nil,使(a nil b)变成(a :whatever b . :whatever)

我们必须使我们的输出功能,这就好比subst,但只影响car -s:

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

(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)))) 

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

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

[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-lispnil符号(一个包和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 

不,不是()对象。请注意0​​函数的返回值如何打印为COMMON-LISP:NILCOMMON-LISP:T。只有当它们出现在当前包中时,符号才会打印出没有包前缀的符号。