拆分格式字符串(格式 t ...)

split format string of (format t ...)

有时候我喜欢用(format t ..)输出一些文字。
为了防止源代码中出现不可读的长格式字符串,并使输出易于对齐,我使用 (format t (concatenate 'string ....).

示例:

(format t (concatenate 'string 
                       "some output~%"
                       "  error-msg: ~a~%"
                       "  uiop-cwd: ~a~%"
                       "  uiop-file-exists: ~a~%")
        "error foo"
        (uiop:getcwd)
        (uiop:file-exists-p "hello_world.bmp"))

在 Common Lisp 中是否有更惯用的编译时方法来做同样的事情?

这是一个等效的格式字符串,它使用了 Tilde newline 格式指令,忽略了后面的换行符和空格(直到下一个可见字符)。为了像你一样缩进空格,我在空格前写了强制换行符 ~%:

(format t 
        "some output~
       ~%   error-msg: ~a~
       ~%   uiop-cwd: ~a~
       ~%   uiop-file-exists: ~a~%"
        "error foo"
        (uiop:getcwd)
        (uiop:file-exists-p "hello_world.bmp"))

(注意。这是单个字符串,因此在编译时无需进行连接。)

你可以用类似的东西做得很好:

(defun fmt (to control/s &rest args-to-format)
  (declare (dynamic-extent args-to-format)) ;?OK
  (apply #'format to (if (listp control/s)
                         (apply #'concatenate 'string control/s)
                       control/s)
         args-to-format))

(define-compiler-macro fmt (&whole form to control/s &rest args-to-format)
  (cond
   ((stringp control/s)
    `(format ,to ,control/s ,@args-to-format))
   ((and (listp control/s)
         (eql (first control/s) 'quote))
    ;; literal
    (destructuring-bind (_ thing) control/s
      (declare (ignore _))
      (print "here")
      (if (and (listp thing) (every #'stringp thing))
          `(format ,to ,(apply #'concatenate 'string thing) ,@args-to-format)
        form)))
   (t
    form)))

编译器宏应该保证

的常见情况
(fmt t '("~&foo: ~S~%"
         "bar~%") ...)

根本没有 运行 时间成本。