4.5 示例:解析日期 (Example: Parsing Dates)

作为序列操作的示例,本节演示了如何写程序来解析日期。我们将编写一个程序,可以接受像是 “16 Aug 1980” 的字符串,然后返回一个表示日、月、年的整数列表。

  1. (defun tokens (str test start)
  2. (let ((p1 (position-if test str :start start)))
  3. (if p1
  4. (let ((p2 (position-if #'(lambda (c)
  5. (not (funcall test c)))
  6. str :start p1)))
  7. (cons (subseq str p1 p2)
  8. (if p2
  9. (tokens str test p2)
  10. nil)))
  11. nil)))
  12. (defun constituent (c)
  13. (and (graphic-char-p c)
  14. (not (char= c #\ ))))

图 4.2 辨别符号 (token)

图 4.2 里包含了某些在这个应用里所需的通用解析函数。第一个函数 tokens ,用来从字符串中取出语元 (token)。给定一个字符串及测试函数,满足测试函数的字符组成子字符串,子字符串再组成列表返回。举例来说,如果测试函数是对字母返回真的 alpha-char-p 函数,我们得到:

  1. > (tokens "ab12 3cde.f" #'alpha-char-p 0)
  2. ("ab" "cde" "f")

所有不满足此函数的字符被视为空白 ── 他们是语元的分隔符,但永远不是语元的一部分。

函数 constituent 被定义成用来作为 tokens 的实参。

在 Common Lisp 里,图形字符是我们可见的字符,加上空白字符。所以如果我们用 constituent 作为测试函数时,

  1. > (tokens "ab12 3cde.f gh" #'constituent 0)
  2. ("ab12" "3cde.f" "gh")

则语元将会由空白区分出来。

图 4.3 包含了特别为解析日期打造的函数。函数 parse-date 接受一个特别形式组成的日期,并返回代表这个日期的整数列表:

  1. > (parse-date "16 Aug 1980")
  2. (16 8 1980)
  1. (defun parse-date (str)
  2. (let ((toks (tokens str #'constituent 0)))
  3. (list (parse-integer (first toks))
  4. (parse-month (second toks))
  5. (parse-integer (third toks)))))
  6. (defconstant month-names
  7. #("jan" "feb" "mar" "apr" "may" "jun"
  8. "jul" "aug" "sep" "oct" "nov" "dec"))
  9. (defun parse-month (str)
  10. (let ((p (position str month-names
  11. :test #'string-equal)))
  12. (if p
  13. (+ p 1)
  14. nil)))

图 4.3 解析日期的函数

parse-date 使用 tokens 来解析日期字符串,接着调用 parse-monthparse-integer 来转译年、月、日。要找到月份,调用 parse-month ,由于使用的是 string-equal 来匹配月份的名字,所以输入可以不分大小写。要找到年和日,调用内置的 parse-integerparse-integer 接受一个字符串并返回对应的整数。

如果需要自己写程序来解析整数,也许可以这么写:

  1. (defun read-integer (str)
  2. (if (every #'digit-char-p str)
  3. (let ((accum 0))
  4. (dotimes (pos (length str))
  5. (setf accum (+ (* accum 10)
  6. (digit-char-p (char str pos)))))
  7. accum)
  8. nil))

这个定义演示了在 Common Lisp 中,字符是如何转成数字的 ── 函数 digit-char-p 不仅测试字符是否为数字,同时返回了对应的整数。