【发布时间】:2017-01-13 02:13:11
【问题描述】:
我正在尝试使用cl-format 来格式化货币。我想要(f 12345.555) ;=> "12,345.56"。我用格式字符串"~$" 得到小数,用"~:D" 得到逗号分隔符。如何组合它们?
【问题讨论】:
标签: clojure common-lisp string-formatting
我正在尝试使用cl-format 来格式化货币。我想要(f 12345.555) ;=> "12,345.56"。我用格式字符串"~$" 得到小数,用"~:D" 得到逗号分隔符。如何组合它们?
【问题讨论】:
标签: clojure common-lisp string-formatting
对于 Common Lisp,我建议使用支持语言环境并定义 ~N 的 cl-l10n。或者,您可以自己滚动:
(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 question 或 this other one)。
【讨论】:
部分问题是~:d 指令仅在传递整数时添加逗号(无论是浮点数还是整数),即如果小数点后有除零以外的任何内容,~:d 只会打印出来数字原样。 CL 的 format 和 Clojure 的 cl-format 都是如此。
解决方法是将数字拆分为整数和小数,然后分别格式化。一种方法是使用truncate 函数,afaik 既不是 Clojure 也不是它的标准库提供的。这是一种方法,使用来自clojure.math.numeric-tower 的floor 和ceil。 (感谢 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 的 cmets 所指出的,在 Common Lisp 中还有其他可能更好的方法来做到这一点。
【讨论】:
(multiple-value-bind (quot rem) (truncate x) ...),而不是拿差价。
round 的错误。