2012-09-15 95 views
2

從Lisp開始,我試圖從文件中讀取矩陣來編程A星算法時遇到了一個問題。 我應該讀取的文件在文件頂部有行數和列數,並且我必須將它們都保存在幾個全局變量中(NM)。文件格式應該是這樣的:用Lisp從文件中讀取矩陣

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)))) 

我會,如果有人真的感謝可以幫助我。

+0

對不起,我不知道爲什麼格式有這種方式。 – user1673162

回答

2

有一堆錯誤。

  1. 你打開一個流,但從來沒有關閉它
  2. 你叫PARSE-INTEGER上的字符,當它需要一個字符串
  3. ​​被調用的符號列表,但它應該是號碼清單。

另外:使用全局變量不是一個好主意。

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會比現在更好。

+0

我在過去幾天一直在使用cl-csv。它看起來很好,並處理所有這些特殊字符/角落案件。並不是說你在這裏有任何東西,但是我會給這個包提供一個穩健性和效率的+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)))) 

第四個問題:使陣列形式的需求數量來設置它的尺寸,但你給它兩個符號來代替。你需要評估這些符號,以獲得您想要的值:

(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,並返回這個類的一個對象。