【问题标题】:Lisp - Print out () instead of nil for empty listLisp - 为空列表打印 () 而不是 nil
【发布时间】:2017-03-20 08:49:34
【问题描述】:

我有一个 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 (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,也没有结果。

【问题讨论】:

  • 请问您为什么需要这样做?

标签: lisp common-lisp


【解决方案1】:

两种可能的解决方案:

我。您可以使用格式指令~: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) 

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

【讨论】:

  • 为了使 pprint 派发工作,*print-pretty* 需要是 T。
【解决方案2】:

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

【讨论】:

  • 在这种情况下,我倾向于专注于 null 类。我想这只是风格上的差异,但是有什么技术原因需要专门研究(eql nil) 吗?便携性?
  • @coredump:只是风格。甚至没有比较性能。我倾向于将 NIL 视为一个对象,而不是代表类型的类的实例。
  • 谢谢,我明白了。
  • 参见l1sp.org/cl/11.1.2.1.2 的第 19 条 - 定义此方法的后果是未定义的。我认为 pprint-dispatch 是要走的路。
  • @Xach:在这种情况下,我不会太在意编写方法,尽管它是一种 hack。我要添加的唯一安全检查,以查看该方法是否已经存在... pprint-dispatch 很棒,但是您正在漂亮地打印列表,这可能是也可能不是人们想要的。要摆脱漂亮的打印(使用更改的调度表)并仍然使用 pprint-dispatch 是困难的。例如,我找不到如何创建一个空的调度表。
【解决方案3】:

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

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

sublissubst 函数将处理树结构,但它们平等对待 conses 的 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

符号 token nil 可以表示除 () 之外的对象的唯一方法是,如果我们在一个尚未从 @987654343 导入 nil 符号的包中@ 包(并且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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-09
    • 2021-08-10
    • 1970-01-01
    • 2022-09-27
    • 2019-01-30
    • 2021-05-06
    相关资源
    最近更新 更多