Testing a Result

An important tool in writing programs is to be able to perform different actions depending on the result of a calculation.

First we need to introduce some functions for testing the value of an expression. Lisp includes a number of functions that give a true or false answer - they are called predicates, and often (but not always) have names ending with p.

In Lisp the convention is that nil, or the empty list, means false, and anything else means true. If you want to represent true you use the special Lisp operator, t.

Testing Lisp objects: eq

The most fundamental Lisp test is eq; it tests whether two Lisp objects are identical. For example:

CL-USER > (eq 'fred 'fred)
T
CL-USER > (eq 'fred 'jim)
NIL

Testing numbers: =, >, and <

There are several functions for testing numbers. For example = tests whether two or more numbers are equal:

CL-USER > (= 2 2)
T
CL-USER > (= 2 3)
NIL

Likewise, > tests if the first number is greater than the second:

CL-USER > (> 4 3)
T

Remember, Lisp uses prefix notation, so the function comes first. In normal maths notation this means 4 > 3.

CL-USER > (> 3 3)
NIL

The eq test can also be used for numbers, but it's more intuitive to use =.

Are numbers even or odd: evenp, oddp

You can test whether a number is even or odd with evenp and oddp. For example:

CL-USER > (evenp 17)
NIL
CL-USER > (oddp 17)
T

Testing whether strings are equal: string=

Doesn't need much explanation:

CL-USER > (string= "cat" "cat")
T

CL-USER > (string= "cat" "dog")
NIL

Is something a number? numberp

You can test whether something is a number with numberp. So:

CL-USER > (numberp 2)
T

CL-USER > (numberp '(1 2 3))
NIL

Is something a list? listp

You can test whether something is a list with listp. For example:

CL-USER > (listp '(1 2 3))
T
CL-USER > (listp 1)
NIL

Note that because nil is equivalent to the empty list it is strictly a list:

CL-USER > (listp nil)
T

But note that:

CL-USER > (listp (+ 1 2))
NIL

because (+ 1 2) is 3, a number not a list. So:

CL-USER > (numberp (+ 1 2))
T

Testing for nil: null

The predicate function null returns t if the parameter is nil, and returns nil otherwise. Remember that nil evaluates to itself, so:

CL-USER > (null nil)
T
CL-USER > (null t)
NIL

Conditional test: if

Now we're ready to use these procedures to perform different actions depending on the value of an expression. Lisp includes several different forms to do this, but the simplest is if.

The form if takes three parameters. It evaluates the first parameter; if it returns a non-nil value, it evaluates and returns the second parameter; otherwise it evaluates and returns the third parameter. 

Here's an example:

(if (evenp a)
  (print "Answer is even")
  (print "Answer is odd"))

This will print one of the two strings depending on whether a is even or odd.

Here's an application of if to find the maximum of two numbers:

(defun maximum (a b)
  (if (> a b)
      a
    b))

Combining tests: and, or, not

You can combine several tests using the procedures and, or, and not.

or can take any number of parameters, and only returns nil if all its parameters evaluate to nil; otherwise it returns T.

and can take any number of parameters, and only returns T if all its parameters evaluate to T; otherwise it returns nil.

not takes one parameters, and returns T if its parameter evaluates to nil; otherwise it returns nil.

For example, to test whether someone is a teenager:

(defun teenager (age)
  (if (and (> age 12) (< age 20))
    "Teenager"
  "Not teenager"))

Grouping procedures: progn

The if form only allows you to give one parameter to be executed if the test is true, and one if the test is false. What happens if you want to do two things in either or both of those situations?

The progn form is useful for this. It allows you to wrap several procedures into one bracketed list.

(if (evenp a)
  (progn (print "Answer is even") 0)
  (progn (print "Answer is odd") 1))

Exercise

1. Test whether a string is a palindrome

Write a procedure palindrome? to test whether a string is a palindrome.

Check that:

(palindrome? "madam")

gives T.

2. Test whether an object is a list of two numbers

Write a procedure numberpair? that returns T if its parameter is a list of two numbers like (2 3), and nil otherwise.

Check that:

(numberpair? '(45 67))

is T, and:

(numberpair? '("cat" "dog"))

is nil.

3. Write a piglatin translator

Improve the piglatin program in Strings to cope with words beginning with a vowel. The rule is that if the word begins with a vowel you add "way" on the end, so for example:

"pig" becomes "igpay"

but

"ant" becomes "antway"


blog comments powered by Disqus