从FORMAT输出到字符串?

时间:2014-07-31 18:54:58

标签: database common-lisp string-formatting

就在最近,我一直在玩lisp,我正在尝试创建一个通用的本地数据库,我正在按照另一个涵盖大部分此类项目的教程,但我决定我想加入一个简单的"特征。

我尝试以自定义格式生成时间戳,并将它们保存为字符串。我尝试将格式生成的字符串存储到使用 defvar 声明的变量中,方法是将变量作为第一个参数调用 format ,但不是保存完整的输出,我只是得到了一个月的那一天。这是我的代码:

(defun date ()

    (setq *SUPPRESS-SIMILAR-CONSTANT-REDEFINITION-WARNING* 1)
    (defconstant *day-names*
    '("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday"))
    *DAY-NAMES*

    (multiple-value-bind
        (second minute hour date month year day-of-week dst-p tz)
        (get-decoded-time)
    (defvar datetime)   
    (format datetime "~2,'0d:~2,'0d:~2,'0d ~a ~d/~2,'0d/~d (GMT~@d)"
    hour
    minute
    second
    (nth day-of-week *day-names*)
    month
    date
    year
    (- tz)
    (return-from date datetime))))

(defun make-entry (category subject idea info researched)
    (defvar date (date))
    (list :category category :subject subject :date date :idea idea :info info :researched researched))

生成条目日期的预期值应类似于 2014年7月30日星期三21:28:18(GMT-7)< / strong>,但我最终只有30,而不是剩下的。此外,我已经测试找到返回的30的类型,它确实是一个整数,而不是char-stream(字符串)应该是。因此,我确信这与格式中的&#34;〜d&#34;有关,但我可能错了。实际上,我只是试图将系统时间的格式化版本传递给make-entry。

3 个答案:

答案 0 :(得分:5)

好的。 [Dons代码审查帽]


首先,让我们更加一致地缩进。你确实做了比典型的lisp-newbie在缩进时做得更好的事情,但是把大部分内容冲到了一列。缩进级别可以为您提供有关程序控制流程的大量提示。一旦你习惯了,我的意思是。

(defun date ()

  (setq *SUPPRESS-SIMILAR-CONSTANT-REDEFINITION-WARNING* 1)
  (defconstant *day-names*
    '("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday"))
  *DAY-NAMES*

  (multiple-value-bind
        (second minute hour date month year day-of-week dst-p tz)
      (get-decoded-time)
    (defvar datetime)   
    (format datetime "~2,'0d:~2,'0d:~2,'0d ~a ~d/~2,'0d/~d (GMT~@d)"
            hour minute second
            (nth day-of-week *day-names*)
            month date year
            (- tz)
            (return-from date datetime))))

(defun make-entry (category subject idea info researched)
  (defvar date (date))
  (list :category category :subject subject :date date :idea idea :info info :researched researched))

可以但不应在本地使用defconstantdefvar。您可以使用let创建这些顶级表单,也可以使其成为本地定义。在您的情况下,constant作为全局变得有意义,而var应该是一个局部变量(稍后会更多)。此外,既然您没有在每个函数调用中重新定义常量,那么您就不需要压制该重定义警告。

(defconstant *day-names* '("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday"))

(defun date ()
  *DAY-NAMES*

  (multiple-value-bind
        (second minute hour date month year day-of-week dst-p tz)
      (get-decoded-time)
    (let ((datetime))
      (format datetime "~2,'0d:~2,'0d:~2,'0d ~a ~d/~2,'0d/~d (GMT~@d)"
              hour minute second
              (nth day-of-week *day-names*)
              month date year
              (- tz)
              (return-from date datetime)))))

Lisp表单自动返回它计算的最后一个值,但任何早期的表单只会产生副作用而不是值。具体来说,*DAY-NAMES*date开头没有任何作用。此外,由于您似乎试图从datetime返回date,因此您无需致电return-from。相反,只需评估datetime

(defun date ()
  (multiple-value-bind
        (second minute hour date month year day-of-week dst-p tz)
      (get-decoded-time)
    (let ((datetime))
      (format datetime "~2,'0d:~2,'0d:~2,'0d ~a ~d/~2,'0d/~d (GMT~@d)"
              hour minute second
              (nth day-of-week *day-names*) 
              month date year
              (- tz))
      datetime)))

format将流作为其第一个参数。变量不是流(虽然它可能包含一个;你的不是)。你似乎想要做的是将当前时间作为字符串返回,根据你的format指令进行格式化。要做到这一点,你根本不需要中间值。只需将NIL作为第一个参数传递给format,然后它会自动创建一个包含您内容的新字符串,然后将其返回。

(defun date ()
  (multiple-value-bind
        (second minute hour date month year day-of-week dst-p tz)
      (get-decoded-time)
    (format nil "~2,'0d:~2,'0d:~2,'0d ~a ~d/~2,'0d/~d (GMT~@d)"
            hour minute second
            (nth day-of-week *day-names*) 
            month date year
            (- tz))))

此时,date似乎做了一些有意义的事情。

cl-user> (date)
"15:39:12 Thursday 7/31/2014 (GMT-5)"
cl-user> 

由于您只是尝试将某些值放入列表中,因此您也不需要创建局部变量来将(date)的结果保存在make-entry中。< / p>

(defun make-entry (category subject idea info researched)
  (list :category category :subject subject :date (date) :idea idea :info info :researched researched))

此时,make-entry返回plist,其:DATE值是来自date的相应格式化字符串。

cl-user> (make-entry "something" "something else" "blah" "bleeh" "stop using side effects")
(:CATEGORY "something" :SUBJECT "something else" :DATE
 "15:41:57 Thursday 7/31/2014 (GMT-5)" :IDEA "blah" :INFO "bleeh" :RESEARCHED
 "stop using side effects")
cl-user> 

(defconstant *day-names* '("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday"))

(defun date ()
  (multiple-value-bind
        (second minute hour date month year day-of-week dst-p tz)
      (get-decoded-time)
    (format nil "~2,'0d:~2,'0d:~2,'0d ~a ~d/~2,'0d/~d (GMT~@d)"
            hour minute second
            (nth day-of-week *day-names*) 
            month date year
            (- tz))))

(defun make-entry (category subject idea info researched)
  (list :category category :subject subject :date (date) :idea idea :info info :researched researched))

如果您跟随the PCL database chapter一起关注,请记住,当您阅读数据库时,需要再次解码以这种方式格式化的日期,假设您为&#39 ;重新计划做除字符串比较以外的任何事情。因此,根据您的具体用例,存储(get-universal-time)的原始输出实际上更有意义。

答案 1 :(得分:1)

(defconstant *day-names* 
  '("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday"))

(defun date ()
  (multiple-value-bind
      (second minute hour date month year day-of-week dst-p tz)
      (get-decoded-time)
    (declare (ignore dst-p))
    (format nil "~2,'0d:~2,'0d:~2,'0d ~a ~d/~2,'0d/~d (GMT~@d)"
            hour minute second
            (nth day-of-week *day-names*)
            month date year (- tz))))

(defun make-entry (category subject idea info researched)
  (list :category category 
        :subject subject 
        :date (date) 
        :idea idea 
        :info info 
        :researched researched))    

然后

? (date)
"21:13:25 Thursday 7/31/2014 (GMT+1)"
? (make-entry "Lisp" "format" "read more" "CLHS" "not yet")
(:CATEGORY "Lisp" :SUBJECT "format" :DATE "21:19:42 Thursday 7/31/2014 (GMT+1)" :IDEA "read more" :INFO "CLHS" :RESEARCHED "not yet")

一些评论:

  1. 在函数之外声明常量
  2. 不要使用defvar声明局部变量,这不符合您的想法 - 使用let,甚至更好,避免它们
  3. 此处不要使用return-from;该函数返回最后评估的表单,在本例中为format
  4. 的结果
  5. 挑剔:如果您有未使用的变量无法避免,请使用(declare (ignore ...))以避免编译错误

答案 2 :(得分:1)

此代码存在许多问题,但最大的问题是format的一个参数是(return-from …)

(format datetime "~2,'0d:~2,'0d:~2,'0d ~a ~d/~2,'0d/~d (GMT~@d)"
    hour
    minute
    second
    (nth day-of-week *day-names*)
    month
    date
    year
    (- tz)
    (return-from date datetime))

将其与:

进行比较
CL-USER> (defun foo ()
           (list 1 2 3 (return-from foo 4)))
FOO
CL-USER> (foo)
4

大多数情况下,您不需要做任何特殊操作来从函数返回值;最后一个表达式的值是函数的结果。这样的东西就足够了,因为当格式的第一个参数是 nil 时,你会得到一个字符串:

(defconstant +day-names+                       ; Some people put + around constants
  '("Monday" "Tuesday" "Wednesday"             ; but it's not as a strong a convention
    "Thursday" "Friday" "Saturday" "Sunday"))  ; as * around dynamic variables.

(defun date ()
  (multiple-value-bind (second minute hour date month year day-of-week dst-p tz)
      (get-decoded-time)
    (declare (ignore dst-p))
    ;; The value of (format ...) is returned from the date.
    (format nil "~2,'0d:~2,'0d:~2,'0d ~a ~d/~2,'0d/~d (GMT~@d)" 
            hour minute second
            (nth day-of-week +day-names+)
            month date year (- tz))))

CL-USER> (date)
"15:36:40 Thursday 7/31/2014 (GMT-5)"

顺便说一句,虽然这有效,但从长远来看,使用某些外部标准格式的日期(例如,由XSD dateTimes指定)可能对您更有用。为此,您可能会找到外部库,例如those for time listed on CLiki中的一些有用。