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