在 Common Lisp 中将数字格式化为特定格式

Formatting number to specific format in Common Lisp

我想将整数(毫秒)转换为以下格式:

HH:MM:SS,SSS

例如时间戳 12,5 秒是 00:00:12,500 和 1 小时 38 分 10 秒这样 01:38:10,000

我对 Lisp 编程还很陌生,但这是我目前所掌握的。

(defun ms->time (ms)
  (let ((hours 0)
        (minutes 0)
        (seconds ms))
    (progn
      (and (>= seconds 3600000)
           (progn
             (setq hours (floor (/ seconds 3600000)))
             (setq seconds (mod seconds 3600000))))
      (and (>= seconds 60000)
           (progn
             (setq minutes (floor (/ seconds 60000)))
             (setq seconds (mod seconds 60000))))
    (format nil "~2,'0d:~2,'0d:~6,'0:d" hours minutes seconds))))

这是简单的输出:

CL> (ms->time 12500)
"00:00:12,500"
CL> (ms->time 5890000)
"01:38:10,000"

看起来很棒,正是我想要的。然而...

CL> (ms->time 0)
"00:00:000000"    
CL> (ms->time 999)
"00:00:000999"

我该如何解决这个问题? format 非常先进,我很确定有一种方法可以满足我的需要。当然,如果您有任何想法如何将我的功能更改为更多 lispy,请不要犹豫。我当前的方法看起来与我的 C++ 版本几乎相同。

谢谢!

一个简单的解决方案是将秒分成它们自己的组成部分,例如小时和分钟。我还使用 LET* 来隐藏 MS 以避免必须使用 SETQ 变量:

(defun ms->time (ms)
  (let* ((hours (floor ms 3600000))
         (ms (mod ms 3600000))
         (minutes (floor ms 60000))
         (ms (mod ms 60000))
         (seconds (floor ms 1000))
         (ms (mod ms 1000)))
    (format nil "~2,'0d:~2,'0d:~2,'0d,~3,'0d"
            hours minutes seconds ms)))

(ms->time 0)
;=> "00:00:00,000"
(ms->time 999)
;=> "00:00:00,999"

我没有检查是否有足够的毫秒数来计算小时或分钟。无论如何,数学都算出来了,我无法想象额外的计算会成为瓶颈。

关于风格(功能改进见jkiiski的回答):

  • 不需要PROGN里面LET
  • 使用AND这种方式不常见,避免使用
  • (floor (/ x a))(floor x a)

你的代码可以写的短一点。

  • top 函数变量可以在 arglist 中指定为 &aux。这对于删除一层括号很有用,方法是去掉 LET.
  • WHEN 而不是 AND
  • SETF 而不是 SETQ - 它可以设置多个地方

例子:

(defun ms->time (ms &aux (hours 0) (minutes 0) (seconds ms))
  (when (>= seconds 3600000)
    (setf hours   (floor seconds 3600000)
          seconds (mod seconds 3600000)))
  (when (>= seconds 60000)
    (setf minutes (floor seconds 60000)
          seconds (mod seconds 60000)))
  (format nil "~2,'0d:~2,'0d:~6,'0:d" hours minutes seconds))

我可能会使用 floor returns 两个值(除法的整数部分,以及提醒)这一事实来做到这一点。

(defun ms->time (ms &optional (stream nil))
  (multiple-value-bind (rest ms) (floor ms 1000)
    (multiple-value-bind (rest s) (floor rest 60)
      (multiple-value-bind (h m) (floor rest 60)
        (format stream "~2,'0d:~2,'0d:~2,'0d,~3,'0d" h m s ms)))))