Variables

Let's consider how you would write a function to convert from dollars to pounds. The procedure is as follows:

  • Multiply the number of dollars by the exchange rate.

The exchange rate is the number of pounds in 1 dollar; let's say today it's 0.61. So our procedure definition is simply:

(defun convert (dollars) (* dollars 0.61))

We call it as follows:

CL-USER > (convert 100)
61.0

In the definition of convert, the symbol dollars is a variable. It's like a box into which we put any value we want to convert to pounds.

Furthermore, it's called a local variable because the value is only available inside the body of the function. If we try and evaluate dollars outside the procedure we get:

CL-USER > dollars
Error: The variable DOLLARS is unbound.

Unbound is another way of saying that it doesn't have a value.

Defining a variable: defparameter

But one problem with convert is that we have to redefine it if the exchange rate changes. What we need is a variable that we can set to the current exchange rate. We can do that with defparameter:

(defparameter exchange-rate 0.61)

Variables defined by defparameter are called global variables because you can refer to them anywhere.

CL-USER > exchange-rate
0.61

So now we can redefine convert to use the exchange-rate variable:

(defun convert (dollars) (* dollars exchange-rate))

A common convention is to give each global variable a name starting and ending with an asterisk, such as *exchange-rate*, to remind you that it's global.

Changing the value of a variable: setf

To change the value of a variable we can use setf. For example, if the exchange rate changes to 0.7:

(setf exchange-rate 0.7)

Now the convert procedure will automatically reflect the new exchange rate:

CL-USER > (convert 100)
70.0

Creating local variables: let*

The let* construct allows you to define one or more variables, with specified initial values, which are local to the body of the let* construct. It is written like this:

(let* ((var1 value1)
       (var2 value2)
       ...)
  body)

where:

  • var1 is the first local variable and value1 is its initial value.
  • var2 is the second local variable and value2 is its initial value, and so on.
  • body is one or more Lisp procedures that can refer to var1 and var2.

As an example of its use, remember our definition of the average procedure:

(defun average (1st-number 2nd-number)
(/ (+ 1st-number 2nd-number) 2))

The parameters 1st-number and 2nd-number are local variables, local to the body of the procedure.

But suppose we wanted to break the calculation into steps, using a local variable sum to store the sum of the two numbers, and another local variable result to store the result of dividing this by 2.

We could do this with let*:

(defun average (1st-number 2nd-number)
  (let* ((sum (+ 1st-number 2nd-number))
         (result (/ sum 2)))
    result))

Note that the asterisk in let* is part of the name - let* is a more general version of an operator called let; we won't worry about the differences at this stage.

Exercises

1. Convert between km and miles

Define a variable using defparameter to represent:

1 kilometer = 0.621371192 miles

Use this to define two procedures, convert-km and convert-miles, that convert miles to km and km to miles, and check that 80 km converts to about 50 miles.

2. Find the average of three numbers

Define a procedure average3 that finds the average of three numbers.

Check that:

(average3 21 7 8)

gives 12.

3. Cube the sum of two numbers

Here's a procedure cubesum that calculates (a + b) x (a + b) x (a + b):

(defun cubesum (a b)
  (* (+ a b) (+ a b) (+ a b)))

Use let* to write it so (a + b) is only calculated once.

Check that:

(cubesum 3 4)

gives 343.

4. Substitute values into a quadratic equation

Write a procedure pseudo-primes that substitutes x into the quadratic equation:

x2 - x + 41

Check that:

(pseudo-primes 40)

gives 1601.


blog comments powered by Disqus