How to make this code simpler, more understandable and more lazy?

I want to parse a text string from a Wavefront OBJ file . Currently, I am only interested in types "V" and "F". My algorithm is as follows:

  • check if the string is nil (otherwise step 2 will fail)
  • leave a comment after "#" and trimming
  • transition prefix "v" or "f"
  • divide the line into a list of elements in which each element
    • splits into a list if it is a character like | 34/76/23 |
    • converted from a list: I take only one element, the first by default
    • or forced to a given type if it is already an atomic number.

Here is the code:

(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 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
1

.

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 #:

(defun remove-comment (line)
  (subseq line 0 (position #\# line)))
+5

Source: https://habr.com/ru/post/1787882/


All Articles