如何在共识列表上使用格式迭代能力

How to use format's iteration ability on a list of conses

我知道我可以使用 format~:{ ~} 运算符直接处理列表列表,例如

CL-USER> (format t "~:{<~A,~A> ~}~%" '((1 2) (3 4)))
<1,2> <3,4>

但现在我有一个缺点列表,例如((1 . 2) (3 . 4)) 和表达式

(format t "~:{<~A,~A> ~}~%" '((1 . 2) (3 . 4)))

导致 SBCL 投诉

The value
  2
is not of type
  LIST 

是否有任何 format 魔术可以在不使用 doloop 的额外迭代的情况下实现这一目的?

我基本上看到了 4 个选项

不要对整个列表使用格式

直接的解决方法是避免问题:

(loop 
  for (k . v) in '((1 . 2) (3 . 4))
  do (format t "<~a,~a> " k v))

自定义格式功能

或者,使用 Tilde Slash 调用打印 cons-cells 的函数:

(defun cl-user::pp-cons (stream cons colonp atsignp)
  (declare (ignore colonp atsignp))
  (format stream "~a, ~a" (car cons) (cdr cons)))

(format nil "~/pp-cons/" (cons 1 2))
=> "1, 2"

请注意,如果您不指定包,则该函数必须在 CL-USER 包中。如果要自定义单元格的打印方式,需要通过特殊变量传递格式:

(defvar *fmt* "(~s . ~s)")

(defun cl-user::pp-cons (stream cons colonp atsignp)
  (declare (ignore colonp atsignp))
  (format stream *fmt* (car cons) (cdr cons)))

然后:

(let ((*fmt* "< ~a | ~a >"))
  (format t "~/pp-cons/" (cons 1 2)))

=> < 1 | 2 >

打印时转换

构建一个新列表,其中不正确的列表被正确的列表替换:

(format t
        "~:{<~a,~a> ~}~%"
        (series:collect 'list
          (series:mapping (((k v) (series:scan-alist '((1 . 2) (3 . 4)))))
            (list k v))))

缺点是转换需要分配内存,只是为了打印。

更改数据格式

如果适当的列表适合打印,也许它们也适合其他操作。许多标准函数都需要适当的列表。请注意,列表 ((1 2) (3 4)) 仍然是一个列表,值只是包装在一个 cons-cell 中。如果您决定从一开始就使用这种格式,则无需转换您的列表。