2011-06-15 74 views
11

有沒有簡單的方法來在浮點數列上對齊小數點?換句話說,我想像的一個輸出端(垂直條「|」是存在僅僅是爲了清楚的目的)垂直對齊浮點數在小數點

(format t "~{|~16,5f|~%~}" '(798573.467 434.543543 2.435 34443.5)) 

| 798573.44000| 
|  434.54355| 
|   2.43500| 
|  34443.50000| 

但與結尾間隔的代替零,如下:

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

回答

5

我不認爲這可以很容易地format的內置控制字符來完成,但你可以通過自己的函數吧:

(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/"

另一個實現使用loop

(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))) 

(免責聲明:也許我忽略了一些的format的文檔更容易。)

+2

我認爲「優雅」勝出的幌子下,這是可以做到完全可以在'格式'框架內通過添加一個函數而不必扔掉整個套件。 – 2011-06-15 20:45:16

+0

請問,我的意思是我執行'my-f',這可能會做得更好。例如,它可能不應該從'1.0'中刪除'0'。當然,格式非常靈活! – danlei 2011-06-15 20:49:22

+0

Somthing很奇怪,爲什麼798573.467顯示爲798573.44? (這種情況也是以普通格式發生的)@danlei – mmj 2011-06-17 07:09:09