如何用cl-format格式化钱(普通lisp格式函数的clojure实现)

时间:2016-09-05 17:14:50

标签: clojure common-lisp string-formatting

我正在尝试使用this.SomeProperty格式化资金。我想要cl-format。我得到格式字符串(f 12345.555) ;=> "12,345.56"的小数,我得到逗号分隔符"~$"。我如何组合它们?

2 个答案:

答案 0 :(得分:6)

使用Common Lisp,我建议使用支持locales的cl-l10n并定义~N。或者,您可以自己动手:

(defun money (stream number colonp atsignp &optional (decimal-places 2))
  (multiple-value-bind (integral decimal) (truncate number)
    (format stream
            (concatenate 'string
                         "~"
                         (and colonp ":")
                         (and atsignp "@")
                         "D"
                         "~0,vf")
            integral
            decimal-places
            (abs decimal))))

(setf *read-default-float-format* 'double-float)

(format nil "~2:@/money/" 123456789.123456789)
=> "+123,456,789.12"

现在,对于Clojure,似乎~/尚不支持cl-format,因此您无法直接复制上述代码。使用Java库可能更快(参见例如this questionthis other one)。

答案 1 :(得分:0)

问题的一部分是~:d指令仅在传递整数时添加逗号(无论它是浮点数还是整数),即如果除了零以外的任何其他值小数点,~:d只是打印出数字。这对于CL format以及Clojure的cl-format来说都是如此。

解决方案是将数字拆分为整数和小数,然后单独格式化。一种方法是使用truncate函数,它既不是Clojure也不是它的标准库提供的。这是一种方法,使用floor中的ceilclojure.math.numeric-tower。 (感谢coredump指出我早期版本中的错误。)

(defn truncate [x] 
  (if (neg? x)
    (ceil x)
    (floor x)))

(defn make-money [x]
  (let [int-part (truncate x)
        dec-part (- x int-part)]
    (cl-format nil "~:d~$" int-part dec-part)))

(make-money 123456789.123456789) ;=> "123,456,7890.12"

请注意,这仅适用于正数。 (编辑:正如哈维在评论中指出的那样,这不是一个解决方案,因为在最后一次评论之后有一个4位数组。)

这回答了OP的问题(编辑:不是真的 - 见上文),但我注意到在Common Lisp中,~$的行为略有不同;默认情况下,它在小数点之前打印出一个初始零(至少在我尝试的实现中 - 不确定这是否标准化)。这可以通过自定义~f指令来避免 - 这在Clojure中也是如此(详见Peter Seibel's introduction):

(defun make-money (x)
  (let* ((int-part (truncate x))
         (dec-part (- x int-part)))
    (format nil "~:d~0,2f" int-part dec-part)))

如果数字太大,您可以使用此定义获得意外结果。我确信有办法通过调整定义来避免这个问题,而且无论如何,正如Joshua Taylor的评论所指出的那样,在Common Lisp中还有其他更好的方法可以做到这一点。