Common lisp 如何使此代码更简单、更清晰和;“更利索”吗;?

Common lisp 如何使此代码更简单、更清晰和;“更利索”吗;?,common-lisp,Common Lisp,我想从中解析文本行。目前我只对“V”和“F”类型感兴趣。 我的算法如下: 检查行是否为非零(否则步骤2将失败) 在“#”之后放置注释并修剪空格 删除前缀“v”或“f” 将字符串拆分为每个元素所在的元素列表 如果是类似于| 34/76/23的符号,则拆分到列表中| 从列表中转换:我只接受一个元素,默认情况下是第一个 或者强制为给定的类型(如果它已经是原子序数) 代码如下: (defun parse-line (line prefix &key (type 'single-float))

我想从中解析文本行。目前我只对“V”和“F”类型感兴趣。 我的算法如下:

  • 检查行是否为非零(否则步骤2将失败)
  • 在“#”之后放置注释并修剪空格
  • 删除前缀“v”或“f”
  • 将字符串拆分为每个元素所在的元素列表
  • 如果是类似于| 34/76/23的符号,则拆分到列表中|
  • 从列表中转换:我只接受一个元素,默认情况下是第一个
  • 或者强制为给定的类型(如果它已经是原子序数)
  • 代码如下:

    (defun parse-line (line prefix &key (type 'single-float))
      (declare (optimize (debug 3)))
      (labels ((rfs (what)
                 (read-from-string (concatenate 'string "(" what ")")))
               (unpack (str &key (char #\/) (n 0))
                 (let ((*readtable* (copy-readtable))) 
                   (when char ;; we make the given char a delimiter (space)
                     (set-syntax-from-char char #\Space))
                   (typecase str
                     ;; string -> list of possibly symbols.
                     ;; all elements are preserved by (map). nil's are dropped
                     (string (delete-if #'null
                                        (map 'list
                                             #'unpack
                                             (rfs str))))
                     ;; symbol -> list of values
                     (symbol (unpack (rfs (symbol-name str))))
                     ;; list -> value (only the requested one)
                     (list (unpack (nth n str)))
                     ;; value -> just coerce to type
                     (number (coerce str type))))))
        (and line
             (setf line (string-trim '(#\Space #\Tab)
                                     (subseq line 0 (position #\# line))))
             (< (length prefix) (length line))
             (string= line prefix :end1 (length prefix) :end2 (length prefix))
             (setf line (subseq line (length prefix)))
             (let ((value (unpack line :char nil))) 
               (case (length value)
                   (3 value)
                   (4 (values (subseq value 0 3) ;; split quad 0-1-2-3 on tri 0-1-2 + tri 0-2-3
                              (list (nth 0 value)
                                    (nth 2 value)
                                    (nth 3 value)))))))))
    
    (定义解析行(行前缀和键(类型为“单浮点”)
    (声明(优化(调试3)))
    (标签)(rfs(什么)
    (从字符串读取(连接“字符串”(“what”)))
    (拆包(str&key(char\/)(n0))
    (let((*readtable*(copy readtable)))
    (当char;;我们将给定的字符作为分隔符(空格)
    (从char#\Space设置语法)
    (打字机)
    ;;字符串->可能的符号列表。
    ;;所有元素都由(map)保存。nil被删除
    (字符串(如果为空则删除
    (地图列表)
    #“打开行李
    (rfs str)
    ;;符号->值列表
    (符号(解包(rfs(符号名称str)))
    ;;列表->值(仅请求的值)
    (列表(打开包装(第n个str)))
    ;值->强制输入
    (编号(强制str类型(()())))
    (和线路
    (setf行(字符串修剪“(#\Space#\Tab)
    (水下第0行(位置线)))
    (<(长度前缀)(长度线))
    (字符串=行前缀:end1(长度前缀):end2(长度前缀))
    (setf线(海底线(长度前缀)))
    (let((值(解包行:char nil)))
    (大小写(长度值)
    (3)价值
    (4)(值(水下值0-3);;三线0-1-2+0-2-3上的四线分割0-1-2-3
    (列表(第n个0值)
    (第n个2值)
    (第n个3值(()()()())()))(
    
    第四步(标签“unpack”)是一种递归过程。它是一个函数,可以自己调用三次

    无论如何,这个解决方案似乎很笨拙


    我的问题是:应该如何用更短更清晰的代码来解决这个问题?

    我会以更结构化的方式来处理这个问题

    您希望将obj文件解析为某种数据结构:

    (defun parse-obj-file (filespec)
      ;; todo
      )
    
    您需要考虑返回的数据结构的外观。现在,让我们返回两个列表的列表,一个顶点,一个面。解析器将遍历每一行,确定它是顶点还是面,然后将其收集到适当的列表中:

    (defun parse-obj-file (filespec)
      (with-open-file (in-stream filespec
                                 :direction :input)
        (loop for line = (read-line in-stream nil)
              while line
              when (cl-ppcre:scan "^v " line)
              collect (parse-vertex line) into vertices
              when (cl-ppcre:scan "^f " line)
              collect (parse-face line) into faces
              finally (return (list vertices faces)))))
    
    我在这里使用了
    cl-ppcre
    库,但您也可以使用
    mismatch
    search
    。然后需要定义
    parse vertex
    parse face
    ,对于这两个属性,
    cl-ppcre:split
    应该非常方便

    为顶点和面定义类可能也很有用

    更新:这是我接近顶点的方式:

    (defclass vertex ()
      ((x :accessor x :initarg :x)
       (y :accessor y :initarg :y)
       (z :accessor z :initarg :z)
       (w :accessor w :initarg :w)))
    
    (defun parse-vertex (line)
      (destructuring-bind (label x y z &optional w)
          (cl-ppcre:split "\\s+" (remove-comment line))
        (declare (ignorable label))
        (make-instance 'vertex
                       :x (parse-number x)
                       :y (parse-number y)
                       :z (parse-number z)
                       :w (parse-number w))))
    
    Parse number
    来自
    Parse number
    库。这比使用
    read
    要好

    更新2:(很抱歉让这成为一个连续的故事;我必须交错一些工作。)一张脸由一个脸点列表组成

    (defclass face-point ()
      ((vertex-index :accessor vertex-index :initarg :vertex-index)
       (texture-coordinate :accessor texture-coordinate
                           :initarg :texture-coordinate)
       (normal :accessor normal :initarg :normal)))
    
    (defun parse-face (line)
      (destructuring-bind (label &rest face-points)
          (cl-ppcre:split "\\s+" (remove-comment line))
        (declare (ignorable label))
        (mapcar #'parse-face-point face-points)))
    
    (defun parse-face-point (string)
      (destructuring-bind (vertex-index &optional texture-coordinate normal)
          (cl-ppcre:split "/" string)
        (make-instance 'face-point
                       :vertex-index vertex-index
                       :texture-coordinate texture-coordinate
                       :normal normal)))
    
    Remove comment
    只需在第一个
    之后扔掉所有内容:


    如何使代码更加简洁?当然,更多的括号。此外,全球搜索并用“th”替换“s”,谢谢您的回答!不幸的是,我不能在这里的注释中显示我的遗留代码。所以我刚刚提到,我从(removecomment…)函数开始,但后来决定,将“视为”更容易。@avp:Lisp读取器应该用于读取Lisp代码。将您的输入信息压缩为
    可读
    是一种相当复杂的方法。但是,这是否会使代码变得更小、更容易理解呢?(我假设文件格式也是正确的。@avp:不,这并不能使它更容易理解。您可以将字符串转换为符号、字符串转换为列表、符号转换为字符串,最后可能是字符串转换为数字,所有这些都是在处理可读表,而不是简单地拆分输入和分析部分。
    (defun remove-comment (line)
      (subseq line 0 (position #\# line)))