寻找允许对列表进行自定义格式的格式字符串

问题描述

所以我有列表,看起来像这样:

((24 . 23) (9 . 6) ... )

并希望将输出自定义格式为如下所示:

"24/23 9/6 ..."

我试过了:

(defun show-pair (ostream pair col-used atsign-used) 
  (declare (ignore col-used atsign-used)) 
  (format ostream "~d/~d" (first pair) (second pair)))

(let ((x '( 1 . 2))) (format nil "~{~/show-pair/~^~}" (list x)))

作为一个简单的热身运动来显示只有一对的列表。但是当在 emacs slime repl 中尝试这个时,我得到了错误

价值 2 不是类型 列表 [TYPE-ERROR 类型的条件]

这当然令人困惑,因为 ~/show-pair/ 预计会处理列表中的一个条目,即对,将一对传递给 show-pair。但看起来,实际上正在发生其他事情。

解决方法

如果你想用 format 来做 - 问题是使用 format 指令访问 alist 的第一个和第二个元素。我没有找到如何在 format 指令中访问它们。

然而,在像 alist 这样有规律的结构中,可以先展平列表,然后让 format-looping 指令在每次循环中消耗两个元素 - 然后一个消耗一对。

由于著名的 :alexandria 库在 Common Lisp 世界中不算作依赖项,因此可以直接使用 alexandria:flatten

(defparameter *al* '((24 . 23) (9 . 6)))
(ql:quickload :alexandria) ;; import alexandria library

(format nil "~{~a/~a~^ ~}" (alexandria:flatten *al*))
;; => "24/23 9/6"

  • nil 以字符串形式返回
  • ~{ ~} 遍历列表
  • ~a/~a 分数
  • ~^ 元素之间的空白区域,但不是在最后一个元素之后

flatten 顺便说一下没有 :alexandria-"dependency" 的情况是:

(defun flatten (l)
  (cond ((null l) nil)
        ((atom l) (list l))
        (t (append (flatten (car l)) (flatten (cdr l))))))
,

在等待反馈的过程中,我发现了问题出在哪里:

到目前为止,我认为 (second x) 的行为与 (cdr x) 完全一样,但是对于标记值,这种假设是错误的。如果我相应地更改了上面(在问题中)的 show-pairs,则一切正常。

因此,这根本不是格式问题,也不是对 ~/foo~/ 的工作原理有任何意外。

(defun show-pair (ostream pair col-used atsign-used) 
  (declare (ignore col-used atsign-used)) 
  (format ostream "~d/~d" (first pair) (cdr pair))) ;; second -> cdr fixes the problem
,

根据我的经验,在 ~/.../ 中使用 format 很少是一个好主意。简单地将您拥有的列表转换为您需要的列表然后直接处理它可能会好得多。因此,例如:

> (format t "~&~:{~D/~D~:^,~}~%"
        (mapcar (lambda (p)
                  (list (car p) (cdr p)))
                '((1 . 2) (3 . 4))))
1/2,3/4
nil

或者如果您想使用 loop

 > (format t "~&~:{~D/~D~:^,~}.~%"
        (loop for (n . d) in '((1 . 2) (3 . 4))
              collect (list n d)))
1/2,3/4.
nil

与打印列表的 I/O 成本相比,与转换列表相关的成本(和存储)可能绝对微不足道。

,

使用来自 Redefinition of the print-object method for conses... 的提示,您可能会得到如下结果:

CL-USER> (let ((std-function (pprint-dispatch 'cons)))
           (unwind-protect 
                (progn (set-pprint-dispatch 
                        'cons 
                        (lambda (s o) (format s "~d/~d" (car o) (cdr o))))
                       (format t "~{~a~%~}" '((23 . 24) (5 . 9))))
             (set-pprint-dispatch 'cons std-function))
           (format t "~{~a~%~}" '((23 . 24) (5 . 9))))
23/24
5/9
(23 . 24)
(5 . 9)
NIL
CL-USER> 

隐藏记账;

(defmacro with-fractional-conses (&body body)
  (let ((std-function (gensym "std-function")))
    `(let ((,std-function (pprint-dispatch 'cons)))
       (unwind-protect 
            (progn (set-pprint-dispatch 
                    'cons 
                    (lambda (s o) (format s "~d/~d" 
                                          (car o) 
                                          (cdr o)))),@body)
         (set-pprint-dispatch 'cons,std-function)))))


CL-USER> (with-fractional-conses 
           (format t "~{~a~%~}" 
                   '((23 . 24) (5 . 9))))
23/24
5/9
NIL
CL-USER>