Lisp 垂直对齐浮动在小数点上

Lisp 垂直对齐浮动在小数点上,lisp,floating-point,format,common-lisp,vertical-alignment,Lisp,Floating Point,Format,Common Lisp,Vertical Alignment,有没有一种简单的方法可以在小数点上对齐一列浮点数?换句话说,我想要一个类似的输出(垂直条“|”只是为了清晰起见) 那是 | 798573.44000| | 434.54355| | 2.43500| | 34443.50000| 但使用尾随空格而不是零,如下所示: | 798573.44 | | 434.54355| | 2.435 | | 34443.5 | 我认为使用格式的内置控制字符不容易做

有没有一种简单的方法可以在小数点上对齐一列浮点数?换句话说,我想要一个类似的输出(垂直条“|”只是为了清晰起见)

那是

|    798573.44000|
|       434.54355|
|         2.43500|
|     34443.50000|
但使用尾随空格而不是零,如下所示:

|    798573.44   |
|       434.54355|
|         2.435  |
|     34443.5    |

我认为使用
格式
的内置控制字符不容易做到这一点,但您可以将自己的函数传递给它:

(defun my-f (stream arg colon at &rest args)
  (declare (ignore colon at))
  (destructuring-bind (width digits &optional (pad #\Space)) args
    (let* ((string (format nil "~v,vf" width digits arg))
           (non-zero (position #\0 string :test #'char/= :from-end t))
           (dot (position #\. string :test #'char= :from-end t))
           (zeroes (- (length string) non-zero (if (= non-zero dot) 2 1)))
           (string (nsubstitute pad #\0 string :from-end t :count zeroes)))
      (write-string string stream))))
您可以这样使用它:

CL-USER> (format t "~{|~16,5/my-f/|~%~}" '(798573.467 434.543543 2.435 34443.5 10))
|    798573.44   |
|       434.54355|
|         2.435  |
|     34443.5    |
|        10.0    |
NIL
填充字符默认为
\Space
,可以作为第三个参数给出,如下所示:
“~16,5”,/my-f/”

使用
循环
的替代实现:

(defun my-f (stream arg colon at &rest args)
  (declare (ignore colon at))
  (loop with string = (format nil "~v,vf" (car args) (cadr args) arg)
        and seen-non-zero = nil
        for i from (1- (length string)) downto 0
        as char = (char string i)
        if (char/= char #\0) do (setq seen-non-zero t)
        collect (if (and (not seen-non-zero)
                         (char= char #\0)
                         (not (char= #\. (char string (1- i)))))
                    (or (caddr args) #\Space)
                    char) into chars
        finally (write-string (nreverse (coerce chars 'string)) stream)))

(免责声明:也许我忽略了
格式
文档中的一些更简单的东西)

我认为“优雅”是在“格式”框架的伪装下胜出的,只需添加一个函数,而不必扔掉整个工具包。威尔,我指的是我的
my-f
实现,也许可以做得更好。例如,它可能不应该从
1.0
中删除
0
。当然,很高兴
格式
如此灵活!奇怪的是,为什么798573.467显示为798573.44?(这种情况也发生在普通格式中)@danleiDanlei,您的第一个优秀解决方案似乎可以很好地处理
1.0
情况,只需稍微修改两行代码(我只报告相关行):
。。。(点位置(位置#\.字符串:test#'char=:from end t))(零(-(长度字符串)位置1(if(eql位置点位置)1 0))…
。你可能想编辑你的原始文章,使之更简单@danleimmj,当然,我会更新答案的。谢谢你的建议!(我将留下第二个实现作为备选方案。)
(defun my-f (stream arg colon at &rest args)
  (declare (ignore colon at))
  (loop with string = (format nil "~v,vf" (car args) (cadr args) arg)
        and seen-non-zero = nil
        for i from (1- (length string)) downto 0
        as char = (char string i)
        if (char/= char #\0) do (setq seen-non-zero t)
        collect (if (and (not seen-non-zero)
                         (char= char #\0)
                         (not (char= #\. (char string (1- i)))))
                    (or (caddr args) #\Space)
                    char) into chars
        finally (write-string (nreverse (coerce chars 'string)) stream)))