I would like to have a variable containing an integer
, that came from an input of a user. It can't accept strings neither decimal numbers.
I would like some help to understand what I am doing wrong here.
My code until now:
I appreciate the help.
(format t "~%Enter a number: ")
(loop (defvar numb (read))
(cond (((rationalp numb)1)
(print "No decimal numbers are allowed, please enter an integer"))
(((stringp numb)1)
(print "No strings are allowed, please enter an integer"))
)
(when ((integerp numb)1) (return numb))
)
Here is how I would do it:
(defun ask-and-read (prompt)
"Prompt the user and read his input."
(princ prompt *query-io*)
(force-output *query-io*) ; flush the buffers
(let ((*read-eval* nil)) ; close the security hole
(read *query-io*)))
(defun request-object (prompt predicate)
"Ask the user for an object using prompt.
Only accept data which satisfies the predicate."
(loop
for object = (ask-and-read prompt)
when (funcall predicate object)
return object
do (format *query-io* "Alas, ~S (~S) does not satisfy ~S, please try again~%"
object (type-of object) predicate)))
Example:
> (request-object "Enter an integer: " #'integerp)
Enter an integer: 4.6
Alas, 4.6 (SINGLE-FLOAT) does not satisfy #<SYSTEM-FUNCTION INTEGERP>, please try again
Enter an integer: 5/7
Alas, 5/7 (RATIO) does not satisfy #<SYSTEM-FUNCTION INTEGERP>, please try again
Enter an integer: asdf
Alas, ASDF (SYMBOL) does not satisfy #<SYSTEM-FUNCTION INTEGERP>, please try again
Enter an integer: 7
==> 7
> (request-object "Enter a real: " #'realp)
Enter a real: 4.5
==> 4.5
> (request-object "Enter a real: " #'realp)
Enter a real: 5/8
==> 5/8
> (request-object "Enter a real: " #'realp)
Enter a real: "sdf"
Alas, "sdf" ((SIMPLE-BASE-STRING 3)) does not satisfy #<SYSTEM-FUNCTION REALP>, please try again
Enter a real: 8
==> 8
Please see the documentation for the facilities I used:
Your code is unreadable because you have incorrect indentation. Lispers do not count parens - this is the job for compilers and editors. We look at indentation. Please do yourself a favor and use Emacs - it will indent the code for you and you will often see your errors yourself.
First of all, defvar
is a top-level form which is used to define global variables, not set them.
Subsequent calls do not change the value:
(defvar *abc* 1)
*abc*
==> 1
(defvar *abc* 10)
*abc*
==> 1 ; not 10!
Use setq
to set variable.
While Lisp does allow global variables, the predominant programming
style in Lisp is the functional style: every function receives its
"input" data as arguments and returns its "output" data as values.
To achieve functional style, prefer a local to a global variable.
You create local variables through let
or
let*
or, in loop
, see
Local Variable Initializations.
You have extra parens and 1
(?!) in your cond
and when
forms.
Remember, parens are meaningful in Lisp.
Binding *read-eval*
to nil
before read
is necessary to
avoid a nuclear war if a user enters #.(launch-nuclear-missiles)
in response to your prompt, because normally read
evaluates whatever
comes after #.
.