【问题标题】:Looking for format string which allows custom formatting for list of pairs寻找允许对列表进行自定义格式化的格式字符串
【发布时间】:2021-01-25 08:06:08
【问题描述】:

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

((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。但看起来,实际上正在发生其他事情。

【问题讨论】:

    标签: common-lisp


    【解决方案1】:

    如果你想用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-“依赖”将在这种情况下:

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

    【讨论】:

    • 是的,这也是我考虑过的一个想法,但我的真实数据有时有超过 2 个元素,有时在某些地方有数字,有时还有符号。因此,我采用了~/.../ 方法,因为调用的函数涉及更多。
    【解决方案2】:

    在等待反馈时,我发现问题出在哪里:

    到目前为止,我认为(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 从不表现得像cdr; it behaves like cadr
    • cdr 的同义词是 rest
    • 这种愚蠢的错误会发生,每当一个人使用其他新事物并专注于那些(比如我第一次使用这种格式化方式)时。
    【解决方案3】:

    根据我的经验,在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 成本相比,与转换列表相关的成本(和存储)可能非常小。

    【讨论】:

      【解决方案4】:

      使用来自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> 
      

      【讨论】:

      • 这(v1)是一种相当危险的方法,因为它本质上是对调度表的浅绑定(实际上:它是表中的浅绑定条目),并且浅绑定很难进行线程-安全,当然不会在这种情况下。更好的方法是制作表的副本,然后将*print-pprint-dispatch* 显式绑定到该副本。然而,这是一个聪明的技巧!
      猜你喜欢
      • 2013-05-11
      • 2014-07-09
      • 2012-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-28
      • 2020-02-02
      • 2015-08-15
      相关资源
      最近更新 更多