Common lisp 正在查找允许对列表进行自定义格式设置的格式字符串

Common lisp 正在查找允许对列表进行自定义格式设置的格式字符串,common-lisp,Common Lisp,我有一些列表,如下所示: CL-USER> (let ((std-function (pprint-dispatch 'cons))) (unwind-protect (progn (set-pprint-dispatch 'cons (lambda (s o) (format s "~d/~d" (car o) (

我有一些列表,如下所示:

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> 
((24.23)(9.6)…)

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

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> 
“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)))
作为一个简单的热身练习,只需1对即可显示列表。但在emacs slime repl中尝试此操作时,我得到了错误

价值 2. 他不是那种人 列表 [类型错误的条件]


这当然令人困惑,因为
~/show pair/
需要处理列表中的一个条目,即pair,将pair传递给
show pair
。但似乎还有其他事情正在发生。

在等待反馈时,我发现了问题的根源:

到目前为止,我认为
(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

在等待反馈时,我发现了问题的根源:

到目前为止,我认为
(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 t "~&~:{~D/~D~:^, ~}~%"
        (mapcar (lambda (p)
                  (list (car p) (cdr p)))
                '((1 . 2) (3 . 4))))
1/2, 3/4
nil
或者如果要使用
循环

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

与打印列表的I/O成本相比,转换列表的成本(和存储)可能非常小。

根据我的经验,以
格式使用
~/…/
几乎不是一个好主意。最好是简单地将您拥有的列表转换为您需要的列表,然后直接进行处理。例如:

> (format t "~&~:{~D/~D~:^, ~}~%"
        (mapcar (lambda (p)
                  (list (car p) (cdr p)))
                '((1 . 2) (3 . 4))))
1/2, 3/4
nil
或者如果要使用
循环

 > (format t "~&~:{~D/~D~:^, ~}.~%"
        (loop for (n . d) in '((1 . 2) (3 . 4))
              collect (list n d)))
1/2, 3/4.
nil
与打印列表的I/O成本相比,转换列表的成本(和存储)可能非常小。

使用来自的提示,您可能会得到如下结果:

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> 
使用来自的提示,您可能会得到如下结果:

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> 

如果要使用
格式
-问题是使用
格式
指令访问列表的第一个和第二个元素。我没有找到如何在
格式
指令中访问它们

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

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

(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
    分数
  • ~^
    元素之间的空格,但不能在最后一个元素之后
展平
顺便说一句,没有
:alexandria
-“依赖性”在这种情况下是:

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

如果要使用
格式
-问题是使用
格式
指令访问列表的第一个和第二个元素。我没有找到如何在
格式
指令中访问它们

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

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

(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
    分数
  • ~^
    元素之间的空格,但不能在最后一个元素之后
展平
顺便说一句,没有
:alexandria
-“依赖性”在这种情况下是:

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

second
的行为从来不像
cdr
cdr
的同义词是
rest
。每当一个人使用其他的、新的东西并专注于这些东西时,就会发生这种愚蠢的错误(就像我第一次使用这种格式)。
second
的行为从来不像
cdr
cdr
同义词是
rest
。每当一个人使用其他新事物并关注这些事物时,就会发生类似的愚蠢错误(就像我第一次使用这种格式)。这(v1)是一种相当危险的方法,因为它本质上是对调度表的浅绑定(实际上:它是表中的浅绑定条目),而浅层绑定很难保证线程安全,在这种情况下肯定不会。更好的方法是创建表的副本,然后显式地将
*print pprint dispatch*
绑定到该副本。然而,这是一个聪明的把戏!这(v1)是一种相当危险的方法,因为它本质上是对分派表的浅绑定(实际上:它是表中的浅绑定条目),而浅绑定很难保证线程安全,在这种情况下肯定不会。更好的方法是创建表的副本,然后显式地将
*print pprint dispatch*
绑定到该副本。然而,这是一个聪明的把戏!是的,我也考虑过这个想法,但我的真实数据有时超过2个元素,有时是数字,有时是符号。