我需要从数据库中读取一个通用Lisp对象的字符串。对象应该是一个包含两个双浮动元素的列表;“(1.0d02.0d0)”,例如:
(let* ((str "(1d0 2d0)")
       (off (read-from-string str)))
  (destructuring-bind (x y)
      off (list x y)))但是,是否存在字符串格式不正确的情况?例如,查询失败,或者对象不存在。代码将给出arg计数错误:
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.0d0);
我做得对吗?有更好的方法来避免这个错误吗?
发布于 2016-12-15 07:03:36
有几种方法可以解决这个问题。一种选择是使用模式匹配库(如琐事)。
(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)如果要检查X和Y是浮动的,可以添加一个保护模式。
(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在他的回答中指出的那样,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结合使用。使用亚历山德里亚作为ENSURE-LIST-function,您可以编写
(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))))发布于 2016-12-15 06:05:01
我对lisp比较陌生,所以你最好等老狗给你答案。下面是我如何重构它的方法。
对于启动程序,还可以检查列表的两个元素是否都是
(floatp element)这会让你的片段看起来像这样
(let* ((str "(1d0 2d0)")
       (off (read-from-string str)))
  (destructuring-bind (x y)
    (if (and off
             (typep off 'list)
             (= 2 (length off)
             (every #'floatp off)) ; here is the new code
        off
        (list 0d0 0d0)) (list x y)))不过,这对眼睛来说已经很难了。让我们把它作为谓词。我假设你想要的是一个坐标。您将使变量名与您的应用程序域匹配。
(defun coord-p (coord)
  (and coord
       (typep coord 'list)
       (= 2 (length coord)
       (every #'floatp coord)))现在,代码片段应该如下所示:
(let* ((str "(1d0 2d0)")
       (coord (read-from-string str)))
  (destructuring-bind (x y)
    (if (coord-p coord)
        coord
        (list 0d0 0d0))
    (list x y)))让我们简化destucturing-bind内部的检查
(defun ensure-coord (coord)
   (if (coord-p coord)
       coord
       (list 0d0 0d0)))现在,代码片段应该如下所示:
(let* ((str "(1d0 2d0)")
       (coord (read-from-string str)))
  (destructuring-bind (x y)
      (ensure-coord coord)
    (list x y)))现在,让我们将代码片段放入函数中:
(defun coord-from-string (str)
  (destructuring-bind (x y)
      (ensure-coord
       (read-from-string str))
    (list x y)))在我看来这挺好的。但实际上..。为什么我们又需要destructuring-bind?到现在为止,它是完全无用的:
(defun coord-from-string (str)
  (ensure-coord
    (read-from-string str))奖金提示:在(:type list)中使用defstruct。
为了进一步清晰和表达,您可以为您的对象声明一个defstruct。例如:
(defstruct (coord (:type list))
   (x 0d0 :type double-float)
   (y 0d0 :type double-float))这样,两个元素的每个列表都可以使用Common提供的用于结构操作的函数来操作。例如:
(coord-y '(1d0 2d0)) ; => 2.0d0注意声明中的(:type list)。这就是为什么我们可以使用一个简单的列表作为一个结构。这样,我们就可以将列表的多功能性与结构的功能性和表现力结合起来。例如,如果您遵循这个技巧,ensure-coord函数将如下所示:
(defun ensure-coord (coord)
  (if (coord-p coord)
      coord
      (make-coord)))可以说,意图更加清晰。
发布于 2016-12-15 12:20:35
请注意,有许多可能的错误来源。例如,读者可以检测错误。
确保处理错误:
(defun get-it (s)
  (let ((*read-eval* nil))      ; don't execute code on reading!
    (flet ((check (it)
             (if (and (listp it)
                      (= (length it) 2)
                      (every #'double-float-p it))
                 it
               (error "data is not a list of two double-floats: ~a" it))))
             (handler-case (check (read-from-string s))
               (error (condition)
                 (princ condition)
                 (list 0.0d0 0.0d0))))))
CL-USER 34 > (get-it "(0.0d0 0.0d0)")
(0.0D0 0.0D0)
CL-USER 35 > (get-it "(0.0d0 0.0d0")
End of file while reading stream #<SYSTEM::STRING-INPUT-STREAM 40E06AD7DB>.
(0.0D0 0.0D0)
CL-USER 36 > (get-it "(0.0d0 foo:aa))")
Reader cannot find package FOO.
(0.0D0 0.0D0)
CL-USER 37 > (get-it ")")
Unmatched right parenthesis.
(0.0D0 0.0D0)
CL-USER 38 > (get-it "(1 2 3)")
data not a list of two double-floats: (1 2 3)
(0.0D0 0.0D0)https://stackoverflow.com/questions/41156543
复制相似问题