Common Lisp - type checking two variables
Asked Answered
A

4

5

Hi I'm a beginner in Common Lisp. I want to check if two variables are integers. If both n and m are integers I want to it to return - if it is negative, 0 if it is zero, + if it is positive and NIL if it is not an integer for both n and m. I figured out how to do this with one variable but I can't seem to figure out how to do it with two variables. Thanks.

This is the code that takes a numeric argument and returns - if it is negative, 0 if it is zero, + if it is positive and NIL if its not an integer:

(defun sign (n)
 (if(typep n 'integer)
    (cond ((< n 0) '-)
          ((= n 0) 0)
          ((> n 0) '+))))

The output for each case is:

CL-USER> (sign 3) 

+

CL-USER> (sign -3) 

-

CL-USER> (sign 0) 

0

CL-USER> (sign 3.3)

NIL

This is the code I have for checking two variables which I want it to check if n and m are integers and if n and m are positive, negative or a zero:

(defun sign (n m)
 (if (and (typep n 'integer) (typep m 'integer))
  (cond (and ((< n 0) '-) ((< m 0) '-))
        (and ((= n 0) 0) ((= m 0) 0))
        (and ((> n 0) '+) ((> m 0) '+)) ))))
Attorn answered 4/10, 2018 at 0:56 Comment(0)
A
6

Remember basic Lisp syntax. Function calls and some basic expressions are written as

(operator argument-0 argument-1 ... argument-n)

Right?

open parenthesis, operator, argument-0 argument-1 ... argument-n, closing parenthesis.

Now if we have (< n 0) and (< m 0) how would an AND expressions look like?

(and (< n 0) (< m 0))

But you write:

and ((< n 0) '-) ((< m 0) '-)

You have these mistakes:

  • no parentheses around the AND expression.
  • extra parenthesis around the argument expressions.
  • '- mixed into the argument expressions.

Now COND expects:

(COND (testa1 forma0 forma1 ... forman)
      (testb1 formb1 formb1 ... formbn)
      ...
      (testm1 formm0 formm1 ... formmn))

So instead of

(defun sign (n m)
  (if (and (typep n 'integer) (typep m 'integer))
      (cond (and ((< n 0) '-) ((< m 0) '-))
            (and ((= n 0)  0) ((= m 0)  0))
            (and ((> n 0) '+) ((> m 0) '+)))))

Btw, there was an extra parenthesis at the end.

We write:

(defun sign (n m)
  (if (and (typep n 'integer) (typep m 'integer))
      (cond ((and (< n 0) (< m 0)) '-)
            .... )))

It's also possible to use predicates like integerp, minusp, zerop and plusp.

Aalborg answered 4/10, 2018 at 4:9 Comment(0)
R
3

You can use the already functioning and tested sign definition - which is typical for the way, lispers program. The first naive solution would be:

(defun sign-for-two (n m)
  (when (eql (sign n) (sign m)) 
    (sign n))

;; (if (condition) return-value NIL)
;; is equivalent to 
;; (when (condition) return-value)

Note, in common lisp it is important, which equality test you choose:

;; only symbols - for object identity        eq
;; symbols or numbers - for object identity  eql
;;   (in most tests the default)
;; eql for each component? also in lists     equal
;; equal not only lists but also
;; arrays (vectors, strings), structures, hash-tables
;; however case-insensitive in case of strings
;;                                           equalp
;; mathematical number equality              =
;; specifically characters                   char=
;; case-sensitive string equality            string=

In our case, eql is sufficient.

;; to avoid `(sign n)` to be evaluated twice,
;; you could store it using `let` 
;; and call from then on the stored value
;; (which is less costly).

(defun sign-for-two (n m)
  (let ((x (sign n)))
    (when (eql x (sign m))
      x)))

Or create an equality tester (default test function: #'eql) which returns the equally tested value and if not equal, NIL:

(defun equality-value (x y &key (test #'eql))
   (when (funcall test z y) z)))

;; and apply this general solution to our case:
(defun sign-for-two (n m)
   (equality-value (sign n) (sign m)))

and you can apply the equality-value function in future for functions where you want to return the value when tested as "equal" and you can give the function via :test whatever equality function other than eql is suitable for that case, like

(equality-value string1 string2 :test #'string=)
Roaring answered 4/10, 2018 at 5:39 Comment(2)
The let inside equality-value is not necessary, but more importantly, calling test should be done with funcall.Pilewort
@Pilewort ah thank you - I always appeciate your answers and comments! - I corrected this (funcall) at console but forgot to correct it at text. And true, the let inside equality-value is superfluous - I will remove it. I was too much thinking in terms of previous code :D .Roaring
A
2

It looks like you have the right approach and just got lost in the parentheses. Each of your cond cases looks like

(and ((< n 0) '-) ((< m 0) '-))

I think you meant

((and (< n 0) (< m 0)) '-)

and the same thing for the other two cases.

Allyn answered 4/10, 2018 at 4:9 Comment(0)
P
1

Another compact way to write sign is to use the standard function signum which

returns one of -1, 0, or 1 according to whether number is negative, zero, or positive

The code could look like:

(defun sign (n)                                                                                                                                                                                                                                                     
  (when (integerp n)                                                                                                                                                                                                                                                            
    (case (signum n)                                                                                                                                                                                                                                                              
      (-1 '-)                                                                                                                                                                                                                                                                     
      (0   0)                                                                                                                                                                                                                                                                       
      (1  '+))))
Polydactyl answered 8/10, 2018 at 9:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.