【问题标题】:How to format money with cl-format (clojure implementation of common lisp format function)如何用cl-format格式化钱(常用lisp格式函数的clojure实现)
【发布时间】:2017-01-13 02:13:11
【问题描述】:

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

【问题讨论】:

标签: clojure common-lisp string-formatting


【解决方案1】:

对于 Common Lisp,我建议使用支持语言环境并定义 ~Ncl-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 questionthis other one)。

【讨论】:

  • 我认为这将是一个简单的格式字符串!像“~:D$”之类的东西。我想我会使用您链接到的那个 java 答案。谢谢。
  • @madstap 格式做了很多事情,我们可能会忘记它并不能做所有事情;-)
【解决方案2】:

部分问题是~:d 指令仅在传递整数时添加逗号(无论是浮点数还是整数),即如果小数点后有除零以外的任何内容,~:d 只会打印出来数字原样。 CL 的 format 和 Clojure 的 cl-format 都是如此。

解决方法是将数字拆分为整数和小数,然后分别格式化。一种方法是使用truncate 函数,afaik 既不是 Clojure 也不是它的标准库提供的。这是一种方法,使用来自clojure.math.numeric-towerfloorceil。 (感谢 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 中还有其他可能更好的方法来做到这一点。

【讨论】:

  • 困扰我的是“round”,它四舍五入到最接近的整数(向下或向上)。使用大于原始数字的整数,您将有一个负的小数部分,整个打印的字符串将是一团糟。
  • @coredump truncate and ftruncate 向零舍入。这就是你想要的行为,对吧?
  • @Mars ROUND(和 TRUNCATE,参见前面的评论)返回多个值。你可以做(multiple-value-bind (quot rem) (truncate x) ...),而不是拿差价。
  • 感谢@coredump 指出使用round 的错误。
  • 谢谢@Xavi。你说得对!我想我会在回复之前尝试修复它,但至少在下个月我不会有时间查看它,所以如果他们来,我会拒绝我的投票。不过,我添加了警告。
猜你喜欢
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-17
  • 2017-10-30
  • 2019-06-18
  • 2016-07-04
  • 1970-01-01
相关资源
最近更新 更多