使用Lisp从文件中读取矩阵

时间:2012-09-15 09:29:32

标签: file matrix lisp

我从Lisp开始,我在尝试从文件中读取矩阵以编程A-star算法时遇到了很大问题。 我应该读取的文件在文件的顶部有行数和列数,我必须将它们保存在几个全局变量中( N M )。文件格式应该是这样的:

ROWS
5
COLUMNS
5
MATRIX
1 1 1 1 1
1 1 0 1 1
1 1 0 0 1
1 1 0 0 1
1 1 1 0 1
START
4 2
GOAL
4 4

此刻我的代码就像这样(顺便说一下,它可能很蹩脚)

(defvar *N*)
(defvar *M*)
(defvar *Goal*)
(defvar *Start*)
(defvar *Matrix*)

(defun read-matrix (file)
  (let ((in (OPEN file :DIRECTION :INPUT)))
    (read-line in nil) 
    (SETQ *N* (parse-integer (read-char in)))
    (read-line in nil)
    (read-line in nil)
    (SETQ *M* (parse-integer (read-line in)))
    (read-line in nil)
    (read-line in nil)
    (SETQ *Matrix* (MAKE-ARRAY '(*N* *M*) :initial-element 1))
    (loop for i from 0 to *N*
        do (loop for j from 0 to *M*
               do (SETF (AREF *Matrix* i j)
                    (read-char in))))
    (read-line in nil)
    (SETQ *Start* (read-line in))
    (read-line in nil)
    (SETQ *Goal* (read-line in))))

如果有人能帮助我,我真的很感激。

3 个答案:

答案 0 :(得分:2)

有一堆错误。

  1. 你打开一个流,但从不关闭它
  2. 当你想要一个字符串
  3. 时,你会在一个字符上调用PARSE-INTEGER 使用符号列表调用
  4. MAKE-ARRAY,但它应该是数字列表。
  5. 另外:使用全局变量不是一个好主意。

答案 1 :(得分:1)

第一个问题:你打开一个文件,但从不关闭它。当您使用open时,您需要记住close之后。由于这是如此常见,因此with-open-file会自动关闭(请参阅CLHS以获取文档)。

(defvar *N*)
(defvar *M*)
(defvar *Goal*)
(defvar *Start*)
(defvar *Matrix*)

(defun read-matrix (file)
  (with-open-file (in file
                      :direction :input)
    (read-line in nil) 
    (setq *N* (parse-integer (read-char in)))
    (read-line in nil)
    (read-line in nil)
    (setq *M* (parse-integer (read-line in)))
    (read-line in nil)
    (read-line in nil)
    (setq *Matrix* (make-array '(*N* *M*) :initial-element 1))
    (loop for i from 0 to *N*
        do (loop for j from 0 to *M*
               do (setf (aref *Matrix* i j)
                    (read-char in))))
    (read-line in nil)
    (setq *Start* (read-line in))
    (read-line in nil)
    (setq *Goal* (read-line in))))

第二个问题:read-char返回一个字符,并且没有为字符定义parse-integer。看来你可以简单地阅读整行。

(defvar *N*)
(defvar *M*)
(defvar *Goal*)
(defvar *Start*)
(defvar *Matrix*)

(defun read-matrix (file)
  (with-open-file (in file
                      :direction :input)
    (read-line in nil) 
    (setq *N* (parse-integer (read-line in)))
    (read-line in nil)
    (read-line in nil)
    (setq *M* (parse-integer (read-line in)))
    (read-line in nil)
    (read-line in nil)
    (setq *Matrix* (make-array '(*N* *M*) :initial-element 1))
    (loop for i from 0 to *N*
        do (loop for j from 0 to *M*
               do (setf (aref *Matrix* i j)
                    (read-char in))))
    (read-line in nil)
    (setq *Start* (read-line in))
    (read-line in nil)
    (setq *Goal* (read-line in))))

第三个问题:你似乎在你需要的那些之间放弃了太多的界限。

(defvar *N*)
(defvar *M*)
(defvar *Goal*)
(defvar *Start*)
(defvar *Matrix*)

(defun read-matrix (file)
  (with-open-file (in file
                      :direction :input)
    (read-line in nil) ; "ROWS"
    (setq *N* (parse-integer (read-line in)))
    (read-line in nil) ; "COLUMNS"
    (setq *M* (parse-integer (read-line in)))
    (read-line in nil) ; "MATRIX"
    (setq *Matrix* (make-array '(*N* *M*) :initial-element 1))
    (loop for i from 0 to *N*
        do (loop for j from 0 to *M*
               do (setf (aref *Matrix* i j)
                    (read-char in))))
    (read-line in nil) ; "START"
    (setq *Start* (read-line in))
    (read-line in nil) ; "GOAL"
    (setq *Goal* (read-line in))))

第四个问题:make-array表单需要数字来设置它的尺寸,但你给它两个符号代替。您需要评估这些符号以获得所需的值:

(defvar *N*)
(defvar *M*)
(defvar *Goal*)
(defvar *Start*)
(defvar *Matrix*)

(defun read-matrix (file)
  (with-open-file (in file
                      :direction :input)
    (read-line in nil) ; "ROWS"
    (setq *N* (parse-integer (read-line in)))
    (read-line in nil) ; "COLUMNS"
    (setq *M* (parse-integer (read-line in)))
    (read-line in nil) ; "MATRIX"
    (setq *Matrix* (make-array (list *N* *M*) :initial-element 1))
    (loop for i from 0 to *N*
        do (loop for j from 0 to *M*
               do (setf (aref *Matrix* i j)
                    (read-char in))))
    (read-line in nil) ; "START"
    (setq *Start* (read-line in))
    (read-line in nil) ; "GOAL"
    (setq *Goal* (read-line in))))

第五个问题:您似乎希望矩阵包含位,但您只需将字符放入其中。最重要的是,您的矩阵甚至不会包含#\1字符),您希望1数字)和#\0您期望0,而是以下(假设是Unix风格的换行符):

#2A((#\1 #\Space #\1 #\Space #\1)
    (#\Space #\1 #\Space #\1 #\Newline)
    (#\1 #\Space #\1 #\Space #\0)
    (#\Space #\1 #\Space #\1 #\Newline)
    (#\1 #\Space #\1 #\Space #\0))

为了达到你想要的效果,我建议读取矩阵线,然后将它们分割成空格并解析字段。一个方便的库是split-sequence

(defvar *N*)
(defvar *M*)
(defvar *Goal*)
(defvar *Start*)
(defvar *Matrix*)

(defun read-matrix (file)
  (with-open-file (in file
                      :direction :input)
    (read-line in nil) ; "ROWS"
    (setq *N* (parse-integer (read-line in)))
    (read-line in nil) ; "COLUMNS"
    (setq *M* (parse-integer (read-line in)))
    (read-line in nil) ; "MATRIX"
    (setq *Matrix* (make-array (list *N* *M*) :initial-element 1))
    (loop :for i :from 0 :to *N*
          :do (let* ((line (read-line in))
                     (fields (split-sequence:split-sequence #\Space line))))
                (loop :for field :in fields
                      :for j :upfrom 0
                      :do (setf (aref *matrix* i j)
                                (parse-integer field))))
    (read-line in nil) ; "START"
    (setq *Start* (read-line in))
    (read-line in nil) ; "GOAL"
    (setq *Goal* (read-line in))))

此时,对于“工作”的某些价值,这应该“有效”(尽管我还没有测试过。)

然而,大约30年前,全球变量用于信息传递已被确定为粗鲁。将其变为更适合强大程序的样式的第一步是从函数返回值。

(defun read-matrix (file)
  (let (n m goal start matrix)
    (with-open-file (in file
                        :direction :input)
      (read-line in nil) ; "ROWS"
      (setf n (parse-integer (read-line in)))
      (read-line in nil) ; "COLUMNS"
      (setf m (parse-integer (read-line in)))
      (read-line in nil) ; "MATRIX"
      (setf matrix (make-array (list n m) :initial-element 1))
      (loop :for i :from 0 :to n
            :do (let* ((line (read-line in))
                       (fields (split-sequence:split-sequence #\Space line))))
                  (loop :for field :in fields
                        :for j :upfrom 0
                        :do (setf (aref matrix i j)
                                  (parse-integer field))))
      (read-line in nil) ; "START"
      (setf start (read-line in))
      (read-line in nil) ; "GOAL"
      (setf goal (read-line in)))
    (values n m goal start matrix)))

我认为你真正需要的是你正在阅读的内容的结构或类。它可能被称为game-state

(defclass game-state ()
  ((rows :accessor rows :initarg :rows)
   (columns :accessor columns :initarg :columns)
   (goal :accessor goal :initarg :goal)
   (start :accessor start :initarg :start)
   (board :accessor board :initarg board)))

然后,您的函数应命名为read-game-state并返回此类的对象。

答案 2 :(得分:0)

让你入门的东西:

(defvar *rows*)
(defvar *columns*)
(defvar *goal*)
(defvar *start*)
(defvar *matrix*)

(defun parse-sequence (tokens parser &key (delimiter #\Space))
  (do ((start 0)
       (end (position delimiter tokens :start 0)
            (position delimiter tokens :start start))
       result)
      ;; you could also read from end to avoid reversing
      ;; I just find this to be more natural
      ((null end)
       (reverse
        (cons
         (funcall parser
                  (subseq tokens start (length tokens)))
                 result)))
    (setf result
          (cons (funcall parser
                         (subseq tokens start end)) result))
    (setf start (1+ end))))

(defun parse-matrix-source (file)
  (with-open-file (stream file :direction :input)
    (do ((line (read-line stream nil :eof)
               (read-line stream nil :eof))
         (matrix-row 0)
         (matrix-column 0 0)
         state)
        ((eq line :eof))
      (cond
        ((string= line "ROWS") (setf state '*rows*))
        ((string= line "COLUMNS") (setf state '*columns*))
        ((string= line "MATRIX") (setf state '*matrix*))
        ((string= line "START") (setf state '*start*))
        ((string= line "GOAL") (setf state '*goal*))
        ((eq state '*rows*) (setf *rows* (parse-integer line)))
        ((eq state '*columns*)
         (setf *columns* (parse-integer line)
               *matrix* (make-array (list *rows* *columns*)
                                    :initial-element 0)))
        ((eq state '*matrix*)
         (dolist (i (parse-sequence line 'parse-integer))
           (setf (aref *matrix* matrix-column matrix-row) i
                 matrix-column (1+ matrix-column)))
         (incf matrix-row))
        ((eq state '*start*)
         (setf *start* (parse-sequence line 'parse-integer)))
        ((eq state '*goal*)
         (setf *goal* (parse-sequence line 'parse-integer)))))))

(defun test-parse-matrix ()
  (parse-matrix-source #P"~/Projects/lisp-doodles/matrix.txt")
  (format t "rows: ~d, columns: ~d, goal: ~s, start: ~s~& matrix: ~s~&"
          *rows* *columns*  *goal* *start* *matrix*))

(test-parse-matrix)

但是我会考虑更好的格式 - 在这种情况下,您很有可能找到一个已经能够解析它的库+将来可以提供更大的灵活性。甚至CSV或INI也会比你现在拥有的更好。