MinSeob Lim
MinSeob Lim

Reputation: 101

Lisp - if statements various actions

This is my lisp code.

 (DEFUN F (A B)
              (SETF C (* 4 A))
              (SETF D (* 2 (EXPT B 3)))
              (SETF RES (+ C D))
              (IF (AND (TYPEP A 'INTEGER) (TYPEP B 'INTEGER))
                  (list 'Final 'value '= res)
                '(YOUR INPUTS ARE NOT NUMBERS)))

For example, (f 5 9) works well. But (f 'w 'q) doesn't work with the following error message:

(ERROR TYPE-ERROR DATUM W EXPECTED-TYPE NUMBER FORMAT-CONTROL ~@<~s' is not of the expected type~s'~:@> FORMAT-ARGUMENTS (W NUMBER)) Error: W' is not of the expected typeNUMBER'

I want to make if A,B is integer calculate 4A+2B^3.

Else if at least one is not an integer print error message.

I try to the code shown above. But how can I make this error handling using if statements?

Upvotes: 0

Views: 870

Answers (1)

jkiiski
jkiiski

Reputation: 8421

First, you should use LET or LET* to define local variables.

(defun f (a b)
  (let* ((c (* 4 a))           ; You need LET* instead of LET because
         (d (* 2 (expt b 3)))  ; RES depends on the previous variables.
         (res (+ c d)))
    (if (and (typep a 'integer) (typep b 'integer))
        (list 'final 'value '= res)
        '(your inputs are not numbers))))

The actual problem is that you're doing the calculations before you check that the arguments are integers. You should move the calculation inside the IF.

(defun f (a b)
  (if (and (integerp a) (integerp b))
      (let* ((c (* 4 a))
             (d (* 2 (expt b 3)))
             (res (+ c d)))
        (list 'final 'value '= res))
      '(your inputs are not numbers)))

Returning lists like that is kind of strange. If you intend them as output for the user, you should instead print the messages and return the actual result.

(defun f (a b)
  (if (and (integerp a) (integerp b))
      (let ((result (+ (* 4 a)
                       (* 2 (expt b 3)))))
        (format t "Final value = ~a~%" result)
        result)                                      ; Return RESULT or
      (format t "Your inputs are not integers.~%"))) ; NIL from FORMAT.

In most cases you should signal an error if the arguments are not correct type. Printing output from a function that does the calculation is usually a bad idea.

(defun f (a b)
  (check-type a integer "an integer")
  (check-type b integer "an integer")
  (+ (* 4 a)
     (* 2 (expt b 3))))

(defun main (a b)
  (handler-case
      (format t "Final value = ~a~%" (f a b))
    ;; CHECK-TYPE signals a TYPE-ERROR if the type is not correct.
    (type-error () (warn "Your inputs are not integers."))))

(main 12 1)
; Final value = 50
;=> NIL
(main 12 'x)
; WARNING: Your inputs are not integers.
;=> NIL

Common Lisp condition system also allows you to use restarts to fix errors. CHECK-TYPE establishes a restart named STORE-VALUE, which you can invoke to supply a correct value for the place. In this case it probably doesn't make sense, but you could do something like use 1 as a default.

(defun main (a b)
  (handler-bind ((type-error (lambda (e)
                               (store-value 1 e))))
    (format t "Final value = ~a~%" (f a b))))

(main 12 1)
; Final value = 50
;=> NIL
(main 12 'x)
; Final value = 50
;=> NIL

Notice that conditions/error handlers do add some overhead, so for performance critical functions you might not want to use them and instead check your arguments before calling the function.

(declaim (ftype (function (integer integer) integer) f))
(defun f (a b)
  (declare (optimize (speed 3) (safety 0) (debug 0)))
  (+ (* 4 a)
     (* 2 (expt b 3))))

(defun main (a b)
  (if (and (integerp a)
           (integerp b))
      (format t "Final value = ~a~%" (f a b))
      (warn "Your inputs are not integers.")))

Upvotes: 5

Related Questions