Defining Global Functions in Lisp

Our guess-my-number game has the computer respond to the player’s request to start the game, and then to requests for either smaller or bigger guesses. For these, we need to define three global functions: guess-my-number, smaller, and bigger. We’ll also define a function to start over with a different number, called start-over. In Common Lisp, functions are defined with defun, like this:

(defun function_name (arguments)
  ...)

First, we specify the name and arguments for the function. Then we follow it up with the code that composes the function’s logic.

Defining the guess-my-number Function

The first function we’ll define is guess-my-number. This function uses the values of the *big* and *small* variables to generate a guess of the player’s number. The definition looks like this:

> (defun guess-my-number ()
      (ash (+ *small* *big*) −1))
 GUESS-MY-NUMBER

The empty parentheses, (), after the function name guess-my-number indicate that this function doesn’t require any parameters.

Although you don’t need to worry about indentation or line breaks when entering code snippets at the REPL, you must be sure to place parentheses correctly. If you forget a parenthesis or put one in the wrong place, you’ll most likely get an error.

Whenever we run a piece of code like this in the REPL, the resulting value of the entered expression will be printed. Every command in Common Lisp generates a return value. The defun command, for instance, simply returns the name of the newly created function. This is why we see the name of the function parroted back to us in the REPL after we call defun .

What does this function do? As discussed earlier, the computer’s best guess in this game will be a number in between the two limits. To accomplish this, we choose the average of the two limits. However, if the average number ends up being a fraction, we’ll want to use a near-average number, since we’re guessing only whole numbers.

We implement this in the guess-my-number function by first adding the numbers that represent the high and low limits, then using the arithmetic shift function, ash, to halve the sum of the limits and shorten the result. The code (+ *small* *big*) adds together those two variables. Because the addition happens within another function call, , the resulting sum is then passed to the ash function.

The parentheses surrounding the ash function and the addition (+) function are mandatory in Lisp. These parentheses are what tell Lisp, “I want you to call this function.”

The built-in Lisp function ash looks at a number in binary form, and then shifts its binary bits to the left or right, dropping any bits lost in the process. For example, the number 11 written in binary is 1011. We can move the bits in this number to the left with ash by using 1 as the second argument:

> (ash 11 1)
22

This produces 22, which is 10110 in binary. We can move the bits to the right (and lop off the bit on the end) by passing in −1 as the second argument:

> (ash 11 −1)
5

This produces 5, which is 101 in binary.

By using the ash function in guess-my-number, we are continually halving our search space of possible numbers to quickly narrow down to the final correct number. As already mentioned, this halving process is called a binary search, a useful technique in computer programming. The ash function is commonly used for such binary searches in Lisp.

Let’s see what happens when we call our new function:

> (guess-my-number)
50

Since this is our first guess, the output we see when calling this function tells us that everything is working as planned: The program picked the number 50, right in between 1 and 100.

When programming in Lisp, you’ll write many functions that won’t explicitly print values on the screen. Instead, they’ll simply return the value calculated in the body of the function. For instance, let’s say we wanted a function that just returns the number 5. Here’s how we could write this:

> (defun return-five ()
     (+ 2 3))

Because the value calculated in the body of the function evaluates to 5, calling (return-five) will just return 5.

This is how guess-my-number is designed. We see this calculated result on the screen (the number 50) not because the function causes the number to display, but because this is a feature of the REPL.

Note

If you’ve used other programming languages before, you may remember having to write something like return... to cause a value to be returned. In Lisp, this is not necessary. The final value calculated in the body of the function is returned automatically.

Defining the smaller and bigger Functions

Now we’ll write our smaller and bigger functions. Like guess-my-number, these are global functions defined with defun:

 > (defun smaller ()
      (setf *big* (1- (guess-my-number)))
      (guess-my-number))
  SMALLER
  > (defun bigger ()
      (setf *small* (1+ (guess-my-number)))
     (guess-my-number))
  BIGGER

First, we use defun to start the definition of a new global function smaller. Because this function takes no parameters, the parentheses are empty .

Next, we use the setf function to change the value of our global variable *big* . Since we know the number must be smaller than the last guess, the biggest it can now be is one less than that guess. The code (1- (guess-my-number)) calculates this: It first calls our guess-my-number function to get the most recent guess, and then it uses the function 1-, which subtracts 1 from the result.

Finally, we want our smaller function to show us a new guess. We do this by putting a call to guess-my-number as the final line in the function body . This time, guess-my-number will use the updated value of *big*, causing it to calculate the next guess. The final value of our function will be returned automatically, causing our new guess (generated by guess-my-number) to be returned by the smaller function.

The bigger function works in exactly the same manner, except that it raises the *small* value instead. After all, if you call the bigger function, you are saying your number is bigger than the previous guess, so the smallest it can now be (which is what the *small* variable represents) is one more than the previous guess. The function 1+ simply adds 1 to the value returned by guess-my-number .

Here we see our functions in action, with the number 56 as our guess:

> (bigger)
75
> (smaller)
62
> (smaller)
56

Defining the start-over Function

To complete our game, we’ll add the function start-over to reset our global variables:

(defun start-over ()
   (defparameter *small* 1)
   (defparameter *big* 100)
   (guess-my-number))

As you can see, the start-over function resets the values of *small* and *big* and then calls guess-my-number again to return a new starting guess. Whenever you want to start a brand-new game with a different number, you can call this function to reset the game.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.164.141