【问题标题】:Age calculation in common LISP from date of birth从出生日期开始计算普通 LISP 的年龄
【发布时间】:2013-12-10 11:15:30
【问题描述】:

我正在尝试使用给定的出生日期(YYYY-MM-DD 形式的字符串)在 Common Lisp 中计算一个人的年龄,但出现以下错误:

错误:"2013-12-10"' is not of the expected typenumber'

我的代码如下

(defun current-date-string ()
  "Returns current date as a string."
  (multiple-value-bind (sec min hr day mon yr dow dst-p tz)
                       (get-decoded-time)
    (declare (ignore sec min hr dow dst-p tz))
    (format nil "~A-~A-~A" yr mon day)))

(defvar (dob  1944-01-01))
  (let ((age (- (current-date-string) dob))))

谁能在这方面给我帮助?我怀疑当前日期与我的输入一样是字符串格式,但是我们如何将其转换为与我输入相同的日期格式?

【问题讨论】:

    标签: lisp common-lisp date-arithmetic


    【解决方案1】:

    您的代码

    有两个直接的问题:

    1. 减法函数-数字 作为参数。您正在传递(current-date-string) 的结果,这是由(format nil "~A-~A-~A" yr mon day) 生成的字符串
    2. 这段代码没有任何意义:

      (defvar (dob  1944-01-01))
        (let ((age (- (current-date-string) dob))))
      

      一方面,它没有正确缩进。有两种形式,第一种是(defvar (dob 1944-01-01))。这不是defvar 的正确语法。 1944-01-01 是一个符号,它没有被绑定,所以即使你已经完成了(defvar dob 1944-01-01),在评估定义时也会出现错误。虽然(let ((age (- (current-date-string) dob)))) 在语法上是正确的,但在绑定年龄之后您并没有任何事情。你可能想要(let ((age (- (current-date-string) dob))) <do-something-here>) 之类的东西。

    日期算术

    无论如何,通用时间都是整数值,

    25.1.4.2 Universal Time

    通用时间是表示为单个时间的绝对时间 非负整数---自一月午夜以来的秒数 1, 1900 GMT(忽略闰秒)。因此时间 1 是 00:00:01(即 是,格林威治标准时间 1900 年 1 月 1 日凌晨 12:00:01)。

    这意味着您可以取两个通用时间并从另一个中减去一个以获得以秒为单位的持续时间。不幸的是,Common Lisp 没有提供用于操作这些持续时间的函数,并且由于闰年等原因,转换它们并非易事。 Common Lisp Cookbook 提到了这一点:

    Dates and Times

    由于世界时间只是数字,因此更容易、更安全 操纵比日历时间。日期和时间应始终是 如果可能,存储为通用时间,并且仅转换为字符串 用于输出目的的表示。例如,它是 直接知道两个日期中的哪一个在另一个之前,通过 只需用

    * (setq *moon* (encode-universal-time 0 17 16 20 7 1969 4))
    2194805820
    
    * (setq *takeoff* (encode-universal-time 0 38 11 28 1 1986 5))
    2716303080
    
    * (- *takeoff* *moon*)
    521497260
    

    这有点超过 5200 万秒,对应于 6035 天,20 小时 21 分钟(您可以通过将该数字除以 60、60 和 24 次)。超越天有点难 因为月和年没有固定的长度,但上面是 大约 16 年半。

    读取日期字符串

    听起来您还需要从YYYY-MM-DD 形式的字符串中读取日期。如果你确信你收到的价值是合法的,你可以做一些简单的事情

    (defun parse-date-string (date)
      "Read a date string of the form \"YYYY-MM-DD\" and return the 
    corresponding universal time."
      (let ((year (parse-integer date :start 0 :end 4))
            (month (parse-integer date :start 5 :end 7))
            (date (parse-integer date :start 8 :end 10)))
        (encode-universal-time 0 0 0 date month year)))
    

    这将返回一个通用时间,您可以按照上述方法进行算术运算。

    懒惰

    作为程序员,懒惰通常是一种很好的做法。在这种情况下,我们可以不自己实现这些函数,而是使用一个为我们处理它的库来偷懒。一些 Common Lisp 实现可能已经提供了日期和时间函数,还有many time libraries listed on Cliki。我不知道其中哪些是最广泛使用的,或者涵盖了您需要的各种功能,并且库建议对于 StackOverflow 来说是题外话,因此您可能需要尝试一些才能找到适合的功能你。

    【讨论】:

    • 我希望我可以对答案的一部分进行投票。您标有“懒惰”的部分需要额外的支持!即使只是第一句话!
    • @verdammelt 我会投赞成票,但我不能把所有功劳都归功于:懒惰是three virtues 之一。 :)
    • 以上代码适用于 1900 世纪的出生日期。但是,如果出生日期来自 1800 世纪,例如“1890-03-02”,LISP 会给出错误“非法日期”。在这方面的任何帮助将不胜感激。谢谢
    • @user3061324 这并不奇怪。正如我在回答中引用的超规范部分所说,通用时间是非负整数,表示自 1900 以来的秒数。世界时不能表示 1900 年之前的日期。这就是为什么最好使用日期和时间库之一而不是普通 Lisp 的世界时。
    • 感谢您的回复
    【解决方案2】:

    不,不能只是从另一个字符串中减去一个字符串并期望计算机神奇地知道这些字符串是一个日期并相应地处理它们。是什么让你产生了这个想法?

    使用库local-time

    使用 Quicklisp 安装:

    (ql:quickload "local-time").
    

    解析日期:

    (local-time:parse-timestring "2013-12-10")
    

    这会产生一个timestamp 对象。

    获取当前时间戳:

    (local-time:now)
    

    以秒为单位获得差异:

    (local-time:timestamp-difference one-timestamp another-timestamp)
    

    要获得年差(向下舍入):

    (local-time:timestamp-whole-year-difference one-timestamp another-timestamp)
    

    【讨论】:

      猜你喜欢
      • 2011-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多