Unbound variable in Lisp

2019-09-14 01:52发布

When I run my code it says that there is an unbound variable in my fill-lib function, but I have no clue what variable is unbound or why.

(defstruct book 
  (title nil)
  (author nil)
  (genre nil))

(setf b (make-book))
(setf lib ())

(defun fill-lib ()
  (setq count 1)
  (with-open-file (s-stream "/Users/David/Desktop/Books.txt"
                            :direction :input)
    (loop
     (cond (> count 1) (return (princ "Library filled")))
     (setf b (make-book))
     (setf (book-title b) (read-line s-stream))
     (setf (book-author b) (read-line s-stream))
     (setf (book-genre b) (read-line s-stream))
     (setf lib (cons b lib))
     (setq count (+ count 1)))))

2条回答
迷人小祖宗
2楼-- · 2019-09-14 02:27

The cond special for has the abstract syntax (cond (<test> [<form> ...])...). That is, each "statement" or "test" needs to be a list, starting with a test form. If the test form is non-nil (that's essentially Common Lisp for "true"), the consequent forms are evaluated in an "implicit progn" (basically "code block"). If there are no consequent forms, the return value would be the value of the test form. If it is nil (Common Lisp for "false"), the next test is evaluated in the same manner.

You have (cond (> count 1) (return (princ "Library filled"))), this is a cond form with two forms, the first being (> count 1), which will be interpreted as "if the variable > is non-nil, first evaluate count, then evaluate 1 and let the result of that last be the result of the cond form".

It would (probably) be cleared if you used when instead of cond here, since (when (> count 1) (return (princ "Library filled"))) would do what I can only assume you want to do (print "Library filled" and return that string from the function).

查看更多
姐就是有狂的资本
3楼-- · 2019-09-14 02:34
(defstruct book 
  (title nil)
  (author nil)
  (genre nil))

(setf b (make-book)) ; b is unbound
(setf lib ())        ; lib is unbound

(defun fill-lib ()
  (setq count 1)     ; count is unbound
  (with-open-file (s-stream "/Users/David/Desktop/Books.txt"
                            :direction :input)
    (loop
     (cond (> count 1) (return (princ "Library filled")))  ; > is unbound, return is unbound
     (setf b (make-book))
     (setf (book-title b) (read-line s-stream))
     (setf (book-author b) (read-line s-stream))
     (setf (book-genre b) (read-line s-stream))
     (setf lib (cons b lib))
     (setq count (+ count 1)))))

So, you have 5 locations where you are working with unbound variables.

For the first three: do not setf unbound variables. Use defparameter or defvar at the top level, if you want to introduce a globally special variable. Use let, let*, or multiple-value-bind for new local bindings (or function parameters, or do loops ...).

For the other two: cond takes clauses that have a condition form as first element and a body as rest, so it would have to be:

(cond ((> count 1) (return (princ "Library filled"))))

For conditionals with only one clause better use when:

(when (> count 1)
  (return (princ "Library filled")))

Improved code:

(defstruct book 
  (title nil)
  (author nil)
  (genre nil))

(defvar *lib* ())

(defun fill-lib (filename count)
  (with-open-file (s-stream filename
                            :direction :input)
    (loop :repeat count
          :for b := (make-book :title (read-line s-stream)
                               :author (read-line s-stream)
                               :genre (read-line s-stream))
          :do (push b *lib*)))
  (princ "Library filled."))
查看更多
登录 后发表回答