我需要从数据库中读取一串 Common Lisp 对象。对象应该是一个包含两个双浮点元素的列表; “(1.0d0 2.0d0)”例如:

(let* ((str "(1d0 2d0)")
       (off (read-from-string str)))
  (destructuring-bind (x y)
      off (list x y)))

但是也有字符串格式不正确的情况?例如,查询失败,或对象不存在。代码将给出 arg-count-error:
error while parsing arguments to DESTRUCTURING-BIND:
  too few elements in
    ()
  to satisfy lambda list
    (X Y):
  exactly 2 expected, but got 0
   [Condition of type SB-KERNEL::ARG-COUNT-ERROR]

我必须使用以下代码进行类型检查。
(let* ((str "(1d0 2d0)")
       (off (read-from-string str)))
  (destructuring-bind (x y)
      (if (and off
               (typep off 'list)
               (= 2 (length off)))
          off
          (list 0d0 0d0)) (list x y)))

如果 str 格式不正确,代码片段将返回默认值。 : (0.0d0 0.0d0) ;

我做得对吗?
有没有更好的方法来避免这个错误?

最佳答案

有几种方法可以解决这个问题。一种选择是使用模式匹配库(例如 Trivia )。

(defun read-coord (string)
  (match (read-from-string string nil)
    ((list x y) (list x y))
    (_ (list 0d0 0d0))))

CL-USER> (read-coord "(1d0 2d0)")
(1.0d0 2.0d0)
CL-USER> (read-coord "(1d0)")
(0.0d0 0.0d0)
CL-USER> (read-coord "")
(0.0d0 0.0d0)

如果要检查 XY 是否为浮点数,可以添加保护模式
(defun read-coord (string)
  (match (read-from-string string nil)
    ((list (guard x (floatp x))
           (guard y (floatp y)))
     (list x y))
    (_ (list 0d0 0d0))))

正如 Rainer Joswig 在 his answer 中指出的那样,READ-FROM-STRING 会导致其他错误,读取时应将 *READ-EVAL* 设置为 NIL
(defun safely-read-from-string (string)
  (let ((*read-eval* nil))
    (ignore-errors (read-from-string string))))

(defun read-coord (string)
  (match (safely-read-from-string string)
    ((list (guard x (floatp x))
           (guard y (floatp y)))
     (list x y))
    (_ (list 0d0 0d0))))

还请记住,您可以将 lambda 列表关键字(例如 &OPTIONAL )与 DESTRUCTURING-BIND 一起使用。使用 Alexandria 作为 ENSURE-LIST 函数,你可以写
(defun read-coord (string)
  (destructuring-bind (&optional (x 0d0) (y 0d0) &rest _)
      (ensure-list (safely-read-from-string string))
    (declare (ignore _))
    (list x y)))

CL-USER> (read-coord "(1d0 2d0)")
(1.0d0 2.0d0)
CL-USER> (read-coord "(1d0)")
(1.0d0 0.0d0)
CL-USER> (read-coord "")
(0.0d0 0.0d0)

如果你不想使用任何库,你可以自己检查一个列表
(defun read-coord (string)
  (let ((coord (safely-read-from-string string)))
    (destructuring-bind (&optional (x 0d0) (y 0d0) &rest _)
        (if (listp coord)
            coord
            (list coord))
      (declare (ignore _))
      (list x y))))

关于common-lisp - 如何避免解构绑定(bind) ARG-COUNT-ERROR?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41156543/

10-13 07:32