How to format money with cl-format (common lisp format函数的clojure实现)
How to format money with cl-format (clojure implementation of common lisp format function)
我正在尝试使用 cl-format
格式化货币。我要(f 12345.555) ;=> "12,345.56"
。我使用格式字符串 "~$"
获取小数,使用 "~:D"
获取逗号分隔符。如何组合它们?
对于 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 or this other one)。
部分问题是 ~:d
指令仅在传递整数(无论是浮点数还是整数)时添加逗号,即如果小数点后有除零以外的任何内容,~:d
只是按原样打印出数字。对于 CL 的 format
和 Clojure 的 cl-format
都是如此。
解决方法是将数字拆分为整数和小数,然后分别格式化。一种方法是使用 truncate
函数,据我所知,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"
请注意,这仅适用于正数。
(编辑:正如 Xavi 在评论中指出的那样,这不是解决方案,因为在最后一条评论之后有一个 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 中还有其他可能更好的方法来做到这一点。
我正在尝试使用 cl-format
格式化货币。我要(f 12345.555) ;=> "12,345.56"
。我使用格式字符串 "~$"
获取小数,使用 "~:D"
获取逗号分隔符。如何组合它们?
对于 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 or this other one)。
部分问题是 ~:d
指令仅在传递整数(无论是浮点数还是整数)时添加逗号,即如果小数点后有除零以外的任何内容,~:d
只是按原样打印出数字。对于 CL 的 format
和 Clojure 的 cl-format
都是如此。
解决方法是将数字拆分为整数和小数,然后分别格式化。一种方法是使用 truncate
函数,据我所知,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"
请注意,这仅适用于正数。 (编辑:正如 Xavi 在评论中指出的那样,这不是解决方案,因为在最后一条评论之后有一个 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 中还有其他可能更好的方法来做到这一点。