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)))))
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).
(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."))